1. Libraries and functions

1.1 Libraries

Load the required libraries.

library(tidyverse)
library(sf)
library(here)
library(readxl)
library(scales)
library(DT)
library(brms)
library(tidybayes)
library(patchwork)
library(marginaleffects)
library(ggrepel)
library(scico)
library(ggdensity)
library(ggpubr)
library(units)
library(glue)
library(ggh4x)

1.2 Helper functions

Functions that we will use throughout the script

#labeller for years
year_labels <- c(1950:1963)

#The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
#Segment for graphs to match ACF period
acf_start <- decimal_date(ymd("1957-03-11"))
acf_end <- decimal_date(ymd("1957-04-12"))

Function for counterfactual plots



plot_counterfactual <- function(model_data, model, population_denominator, outcome, grouping_var=NULL, re_formula,...){
  
  #labeller for years
  year_labels <- c(1950:1964) #extra year for the extant of the x-axis

  #The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
  #Segment for graphs to match ACF period
  acf_start <- decimal_date(ymd("1957-03-11"))
  acf_end <- decimal_date(ymd("1957-04-12"))

  summary <- {{model_data}} %>%
    select(year, year2, y_num, acf_period, {{population_denominator}}, {{outcome}}, {{grouping_var}}) %>%
    add_epred_draws({{model}}, re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
          .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
          .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))



  #create the counterfactual (no intervention), and summarise
  
  counterfact <-
    add_epred_draws(object = {{model}},
                    newdata = {{model_data}} %>%
                                  select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, {{outcome}}) %>%
                                  mutate(acf_period = "a. pre-acf"), re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
         .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
         .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))
  


  #plot the intervention effect
p <- summary %>%
    droplevels() %>%
    ggplot() +
    geom_vline(aes(xintercept=acf_start, linetype="Mass CXR screening intervention")) +
    geom_vline(aes(xintercept=acf_end, linetype="Mass CXR screening intervention")) +
    geom_ribbon(aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
    geom_ribbon(data = counterfact %>% filter(year>=1956), 
                aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
    geom_line(data = counterfact %>% filter(year>=1956), 
              aes(y=.epred_inc, x=year2, colour="Counterfactual")) +
    geom_line(aes(y=.epred_inc, x=year2, group=acf_period,  colour=acf_period)) +
    geom_point(data = {{model_data}} %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")), 
               aes(y={{outcome}}, x=year2, shape=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
    theme_grey() +
    scale_y_continuous(labels=comma, limits =c(0,400)) +
    scale_x_continuous(labels = year_labels,
                       breaks = year_labels) +
    scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_shape_discrete(name="Empirical data (period):", na.translate = F) +
    scale_linetype_manual(values = 2, name="") +
    labs(
      x = "",
      y = "CNR (per 100,000)"
    ) +
    guides(x = "axis_truncated", y = "axis_truncated") +
    theme(legend.position = "bottom",
          legend.box="vertical", 
          text = element_text(size=10),
          axis.text.x = element_text(size=10, angle = 90, hjust=1, vjust=0.5),
          legend.text = element_text(size=10),
          legend.spacing.y = unit(0.1, 'cm'),
          axis.line = element_line(colour = "black")) 

    facet_vars <- vars(...)

  if (length(facet_vars) != 0) {
    p <- p + facet_wrap(facet_vars)
  }
  p

}

Function for calculating measures of change over time (RR.peak, RR.level, RR.slope)


summarise_change <- function(model_data, model, population_denominator, grouping_var = NULL, re_formula = NULL) {
  
  #functions for calculating RR.peak
  #i.e. relative case notification rate in 1957 vs. counterfactual trend for 1957
  
  grouping_var <- enquo(grouping_var)
  
  if (!is.null({{grouping_var}})) {
    
    #make the prediction matrix, conditional on whether we want random effects included or not.
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  } else {
    
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  }
  
  peak_draws <- add_epred_draws(newdata = out,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
  
  peak_summary <- peak_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")
  
  
  #functions for calculating RR.level
  #i.e. relative case notification rate in 1958 vs. counterfactual trend for 1958
  
    if (!is.null({{grouping_var}})) {
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    level_draws <- add_epred_draws(newdata = out2,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    arrange(y_num, .draw) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
  
  level_summary <- level_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")
    
    
  #functions for calculating RR.slope
  #i.e. relative change in case notification rate in 1958-1963 vs. counterfactual trend for 1959-1963
  
    if (!is.null({{grouping_var}})) {
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    slope_draws <- add_epred_draws(newdata = out3,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
        arrange(y_num) %>%
        ungroup() %>%
        mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
        group_by(.draw, y_num, !!grouping_var) %>%
        summarise(slope = last(epred_cnr)/first(epred_cnr)) %>%
        ungroup() %>%
        group_by(.draw, !!grouping_var) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
  
  slope_summary <- slope_draws %>%
     group_by(!!grouping_var) %>%
      mean_qi(estimate) %>%
      mutate(measure = "RR.slope")
    
  #gather all the results into a named list
    lst(peak_draws=peak_draws, peak_summary=peak_summary, 
        level_draws=level_draws, level_summary=level_summary, 
        slope_draws=slope_draws, slope_summary=slope_summary)
  
}

Function for calculating difference from counterfactual


calculate_counterfactual <- function(model_data, model, population_denominator, grouping_var=NULL, re_formula=NA){
  
  #effect vs. counterfactual
  counterfact <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc_counterf = .epred/{{population_denominator}}*100000, .epred_counterf=.epred)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, .draw, .epred_counterf, .epred_inc_counterf, {{grouping_var}})
  
  #Calcuate case notification rate per draw, then summarise.
  post_change <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc = .epred/{{population_denominator}}*100000)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, {{grouping_var}}, .draw, .epred, .epred_inc, {{grouping_var}}) 
  
  #for the overall period
    counterfact_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, .draw, .epred, {{grouping_var}})  %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred_counterf = sum(.epred)) 
  
  #Calcuate case notification rate per draw, then summarise.
  post_change_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, {{grouping_var}}, .draw, .epred) %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred = sum(.epred)) 
  
  
counter_post <-
  left_join(counterfact, post_change) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf,
           diff_inc100k = .epred_inc - .epred_inc_counterf,
           rr_inc100k = .epred_inc/.epred_inc_counterf) %>%
    group_by(year, {{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change, diff_inc100k, rr_inc100k) %>%
    ungroup()

counter_post_overall <-
  left_join(counterfact_overall, post_change_overall) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by({{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup()

lst(counter_post, counter_post_overall)

}

Function for tidying up counterfactuals (mostly for making nice tables)


tidy_counterfactuals <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"),
            diff_inc = glue::glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr_inc = glue::glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"))
}


tidy_counterfactuals_overall <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"))
}

2. Data

Import datasets for analysis

2.1 Shapefiles

Make a map of Glasgow wards


glasgow_wards_1951 <- st_read(here("mapping/glasgow_wards_1951.geojson"))
Reading layer `glasgow_wards_1951' from data source 
  `/Users/peter.macpherson/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/glasgow_wards_1951.geojson' 
  using driver `GeoJSON'
Simple feature collection with 37 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -4.393502 ymin: 55.77464 xmax: -4.070411 ymax: 55.92814
Geodetic CRS:  WGS 84

#read in Scotland boundary
scotland <- st_read(here("mapping/Scotland_boundary/Scotland boundary.shp"))
Reading layer `Scotland boundary' from data source 
  `/Users/peter.macpherson/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/Scotland_boundary/Scotland boundary.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 5513 ymin: 530249 xmax: 470332 ymax: 1220302
Projected CRS: OSGB36 / British National Grid
#make a bounding box for Glasgow
bbox <- st_bbox(glasgow_wards_1951) |> st_as_sfc()

#plot scotland with a bounding box around the City of Glasgow
scotland_with_bbox <- ggplot() +
  geom_sf(data = scotland, fill="antiquewhite") +
  geom_sf(data = bbox, colour = "#C60C30", fill="antiquewhite") +
  theme_void() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "#EAF7FA", size = 0.3))

#plot the wards
#note we tidy up some names to fit on map
glasgow_ward_map <- glasgow_wards_1951 %>%
  mutate(ward = case_when(ward=="Shettleston and Tollcross" ~ "Shettleston and\nTollcross",
                          ward=="Partick (West)" ~ "Partick\n(West)",
                          ward=="Partick (East)" ~ "Partick\n(East)",
                          ward=="North Kelvin" ~ "North\nKelvin",
                          ward=="Kinning Park" ~ "Kinning\nPark",
                          TRUE ~ ward)) %>%
  
  ggplot() +
  geom_sf(aes(fill=division)) +
  geom_sf_label(aes(label = ward), size=3, fill=NA, label.size = NA, colour="black") +
  #scale_colour_identity() +
  scale_fill_brewer(palette = "Set3", name="City of Glasgow Division") +
  theme_grey() +
  labs(x="",
       y="",
       fill="Division") +
  theme(legend.position = "top",
        
        panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "antiquewhite", size = 0.3),
        panel.grid.major = element_line(color = "grey78")) +
  guides(fill=guide_legend(title.position = "top", title.hjust = 0.5, title.theme = element_text(face="bold")))

#add the map of scotland as an inset
glasgow_ward_map + inset_element(scotland_with_bbox, 0.75, 0, 1, 0.4)
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data
ggsave(here("figures/s1.tiff"), height=10, width = 12)
Warning in st_point_on_surface.sfc(sf::st_zm(x)) :
  st_point_on_surface may not give correct results for longitude/latitude data

3. Denominators

Load in the datasets for denonomiators, and check for consistency.


overall_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "overall_population")

overall_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
overall_pops <- overall_pops %>%
  mutate(year2 = year+0.5)

Note, we have three population estimates:

  1. Population without institutionalised people or people in shipping
  2. Population in institutions
  3. Population in shipping

(Population in shipping is estimated from the 1951 census, so is the same for most years)

3.1 Overall population

First, plot the total population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2), alpha=0.5, colour = "mediumseagreen", fill="mediumseagreen") +
  geom_point(aes(y=total_population, x=year2), colour = "mediumseagreen") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: total population",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

Now the population excluding institutionalised and shipping population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=population_without_inst_ship, x=year2), alpha=0.5, colour = "purple", fill="purple") +
  geom_point(aes(y=population_without_inst_ship, x=year2), colour = "purple") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: population excluding institutionalised and shipping",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

3.2 Population by Ward

There are 5 Divisions containing 37 Wards in the Glasgow Corporation, with consistent boundaries over time.

#look-up table for divisions and wards
ward_lookup <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "divisions_wards")


ward_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "ward_population")

ward_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
ward_pops <- ward_pops %>%
  mutate(year2 = year+0.5)

#Get the Division population
division_pops <- ward_pops %>%
  group_by(division, year) %>%
  summarise(population_without_inst_ship = sum(population_without_inst_ship, na.rm = TRUE),
            institutions = sum(institutions, na.rm = TRUE),
            shipping = sum(shipping, na.rm = TRUE),
            total_population = sum(total_population, na.rm = TRUE))
`summarise()` has grouped output by 'division'. You can override using the `.groups` argument.
division_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Plot the overall population by Division and Ward


division_pops %>%
  mutate(year2 = year+0.5) %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division), alpha=0.8) +
  geom_point(aes(y=total_population, x=year2, colour=division)) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(division~.) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name = "") +
  scale_colour_brewer(palette = "Set3", name = "") +
  labs(
    title = "Glasgow Corporation: total population by Division",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

ward_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division)) +
  geom_point(aes(y=total_population, x=year2, colour=division), colour="black") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(ward~., ncol=6) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name="Division") +
  scale_colour_brewer(palette = "Set3", name = "Division") +
  labs(
    x = "",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

ggsave(here("figures/s3.tiff"), height=14, width=12)

Approximately, how many person-years of follow-up do we have?


overall_pops %>%
  ungroup() %>%
  summarise(across(year, length, .names = "years"),
            across(c(population_without_inst_ship, total_population), sum)) %>%
  mutate(across(where(is.double), comma)) %>%
  datatable()
NA
NA

Change in population by ward


ward_pops %>%
  group_by(ward) %>%
  summarise(pct_change_pop = (last(population_without_inst_ship) - first(population_without_inst_ship))/first(population_without_inst_ship)) %>%
  mutate(pct_change_pop = percent(pct_change_pop)) %>%
  arrange(pct_change_pop) %>%
  datatable()
NA
NA
NA

3.3 Population by age and sex


age_sex <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "age_sex_population") %>%
  pivot_longer(cols = c(male, female),
               names_to = "sex")

#collapse down to smaller age groups to be manageable
age_sex <- age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age == "0 to 4" ~ "00 to 04",
                         age == "5 to 9" ~ "05 to 14",
                         age == "10 to 14" ~ "05 to 14",
                         age == "15 to 19" ~ "15 to 24",
                         age == "20 to 24" ~ "15 to 24",
                         age == "25 to 29" ~ "25 to 34",
                         age == "30 to 34" ~ "25 to 34",
                         age == "35 to 39" ~ "35 to 44",
                         age == "40 to 44" ~ "35 to 44",
                         age == "45 to 49" ~ "45 to 59",
                         age == "50 to 54" ~ "45 to 59",
                         age == "55 to 59" ~ "45 to 59",
                         TRUE ~ "60 & up")) %>%
  group_by(year, age, sex) %>%
  mutate(value = sum(value)) %>%
  ungroup()



m_age_sex <- lm(value ~ splines::ns(year, knots = 3)*age*sex, data = age_sex)

summary(m_age_sex)
Warning in summary.lm(m_age_sex) :
  essentially perfect fit: summary may be unreliable

Call:
lm(formula = value ~ splines::ns(year, knots = 3) * age * sex, 
    data = age_sex)

Residuals:
       Min         1Q     Median         3Q        Max 
-2.107e-10 -7.560e-13  0.000e+00  0.000e+00  2.107e-10 

Coefficients: (14 not defined because of singularities)
                                                    Estimate Std. Error    t value Pr(>|t|)    
(Intercept)                                        5.222e+04  3.820e-10  1.367e+14   <2e-16 ***
splines::ns(year, knots = 3)1                     -8.043e+03  7.621e-10 -1.055e+13   <2e-16 ***
splines::ns(year, knots = 3)2                             NA         NA         NA       NA    
age05 to 14                                        3.669e+04  4.679e-10  7.843e+13   <2e-16 ***
age15 to 24                                       -3.893e+03  4.679e-10 -8.320e+12   <2e-16 ***
age25 to 34                                       -3.996e+04  4.679e-10 -8.540e+13   <2e-16 ***
age35 to 44                                       -4.230e+04  4.679e-10 -9.040e+13   <2e-16 ***
age45 to 59                                        5.459e+04  4.411e-10  1.238e+14   <2e-16 ***
age60 & up                                         7.533e+04  4.126e-10  1.826e+14   <2e-16 ***
sexmale                                            3.374e+03  5.402e-10  6.244e+12   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14         -1.863e+03  9.334e-10 -1.996e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24          7.533e+04  9.334e-10  8.070e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34          1.325e+05  9.334e-10  1.420e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44          1.380e+05  9.334e-10  1.479e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59          3.474e+03  8.800e-10  3.948e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up          -8.453e+04  8.232e-10 -1.027e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up                  NA         NA         NA       NA    
splines::ns(year, knots = 3)1:sexmale             -1.994e+03  1.078e-09 -1.850e+12   <2e-16 ***
splines::ns(year, knots = 3)2:sexmale                     NA         NA         NA       NA    
age05 to 14:sexmale                                1.053e+04  6.617e-10  1.592e+13   <2e-16 ***
age15 to 24:sexmale                                2.352e+04  6.617e-10  3.555e+13   <2e-16 ***
age25 to 34:sexmale                                1.355e+04  6.617e-10  2.047e+13   <2e-16 ***
age35 to 44:sexmale                               -1.727e+03  6.617e-10 -2.611e+12   <2e-16 ***
age45 to 59:sexmale                                2.774e+03  6.238e-10  4.446e+12   <2e-16 ***
age60 & up:sexmale                                -7.761e+04  5.835e-10 -1.330e+14   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14:sexmale -2.049e+04  1.320e-09 -1.552e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24:sexmale -6.780e+04  1.320e-09 -5.136e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34:sexmale -3.804e+04  1.320e-09 -2.882e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44:sexmale -1.171e+04  1.320e-09 -8.874e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59:sexmale -3.473e+04  1.244e-09 -2.791e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up:sexmale   1.056e+05  1.164e-09  9.071e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up:sexmale          NA         NA         NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.755e-11 on 44 degrees of freedom
Multiple R-squared:      1, Adjusted R-squared:      1 
F-statistic: 1.714e+29 on 27 and 44 DF,  p-value: < 2.2e-16
age_levels <- age_sex %>% select(age) %>% distinct() %>% pull() 

age_sex_nd <- 
  crossing(
    age=age_levels,
    sex=c("male", "female"),
    year = 1950:1963
  )

pred_pops <- age_sex_nd %>% modelr::add_predictions(m_age_sex)

pred_pops %>%
  ggplot(aes(x=year, y=pred, colour=age)) +
  geom_line() +
  geom_point() +
  facet_grid(sex~.) +
  scale_y_continuous(labels = comma, limits = c(0, 125000))


#How well do they match up with our overall populations?
pred_pops %>%
  group_by(year) %>%
  summarise(sum_pred_pop = sum(pred)) %>%
  right_join(overall_pops) %>%
  select(year, sum_pred_pop, population_without_inst_ship, total_population) %>%
  pivot_longer(cols = c(sum_pred_pop, population_without_inst_ship, total_population)) %>%
  ggplot(aes(x=year, y=value, colour=name)) +
  geom_point() +
  scale_y_continuous(labels = comma, limits = c(800000, 1250000))
Joining with `by = join_by(year)`

pred_pops %>%
  group_by(year, sex) %>%
  summarise(sum = sum(pred)) %>%
  group_by(year) %>%
  mutate(sex_ratio = first(sum)/last(sum))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

Population pyramids


label_abs <- function(x) {
  comma(abs(x))
}


pred_pops %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(year_pop = sum(pred),
         age_sex_pct = percent(pred/year_pop, accuracy=0.1)) %>%
  mutate(sex = case_when(sex=="male" ~ "Male",
                         sex=="female" ~ "Female")) %>%
  ggplot(
    aes(x = age, fill = sex, 
        y = ifelse(test = sex == "Female",yes = -pred, no = pred))) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = age_sex_pct),
            position= position_stack(vjust=0.5), colour="black", size=2.5) +
  facet_wrap(year~., ncol=7) +
  coord_flip() +
  scale_y_continuous(labels = label_abs) +
  scale_fill_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  theme_ggdist() +
  theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5),
        legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="") 


ggsave(here("figures/s4.tiff"), width=10)
Saving 10 x 4.51 in image

Not perfect, but resonably good. But ahhhhh… the age groups don’t align with the case notification age groups! Come back to think about this later.

4. Tuberculosis cases

Import the tuberculosis cases dataset

4.1 Overall notifications

Overall, by year.


cases_by_year <- read_xlsx("2023-11-28_glasgow-acf.xlsx", sheet = "by_year")

cases_by_year%>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


#shift year to midpoint
cases_by_year <- cases_by_year %>%
  mutate(year2 = year+0.5)

Plot the overall number of case notified per year, by pulmonary and extra pulmonary classification.


cases_by_year %>%
  select(-total_notifications, -year) %>%
  pivot_longer(cols = c(pulmonary_notifications, `non-pulmonary_notifications`)) %>%
  mutate(name = case_when(name == "pulmonary_notifications" ~ "Pulmonary TB",
                          name == "non-pulmonary_notifications" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

4.2 Notifications by Division

Read in the datasets and merge together.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
ward_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_ward", value)) %>%
  pull(value)


cases_by_ward_sex_year <- map_df(ward_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_ward_sex_year %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Aggregate together to get cases by division


cases_by_division <- cases_by_ward_sex_year %>%
  left_join(ward_lookup) %>%
  group_by(division, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE))
Joining with `by = join_by(ward)`
`summarise()` has grouped output by 'division', 'year'. You can override using the `.groups` argument.
#shift year to midpoint
cases_by_division <- cases_by_division %>%
  mutate(year2 = year+0.5) %>%
  ungroup()

cases_by_division  %>%
  select(-year2) %>%
  select(year, everything()) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


cases_by_division %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

4.3 Notifications by ward



cases_by_ward <- cases_by_ward_sex_year %>%
  group_by(ward, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'ward', 'year'. You can override using the `.groups` argument.
cases_by_ward %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  select(year, everything()) %>%
  datatable()

#shift year to midpoint
cases_by_ward <- cases_by_ward %>%
  mutate(year2 = year+0.5)

cases_by_ward %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.8) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA

4.4 Notifications by age and sex

As we don’t have denominators, we will just model the change in counts.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
age_sex_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_age_sex", value)) %>%
  pull(value)


cases_by_age_sex <- map_df(age_sex_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_age_sex %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA

4.5 Uptake of screening

What percentage of adults (15+ participated in the intervention in 1957)?

Note that in the Report of Sir Kenneth Cowan, we have the following estimates of participation (we will use these for the manuscript, as they are not based on my estimates)

male_adult_resident_participation <- 281875
female_adult_resident_participation <- 340474
male_adult_resident_population <- 381713
female_adult_resident_population <- 437588

#overall participation
(male_adult_resident_participation+female_adult_resident_participation)/(male_adult_resident_population+female_adult_resident_population)
[1] 0.7596097
#male participation
male_adult_resident_participation/male_adult_resident_population
[1] 0.7384475
#female participation
female_adult_resident_participation/female_adult_resident_population
[1] 0.7780698

Look at uptake of screening by age and sex



uptake_age_sex <- read_xlsx("2024-03-26_mass_xray_uptake.xlsx", sheet = "uptake_age_sex")

uptake_graph <- uptake_age_sex %>%
  mutate(uptake = examined/resident_population) %>%
  mutate(examined_l = comma(examined),
         resident_population_l = comma(resident_population),
         uptake_l = percent(uptake, accuracy=0.1)) %>%
  mutate(label = glue("{examined_l}/{resident_population_l} ({uptake_l})")) %>%
  filter(age !="00-14") %>%
  mutate(sex = case_when(sex=="m" ~ "Male",
                         sex=="f" ~ "Female")) %>%
  ggplot(aes(x=age, y=uptake, group=sex, fill=sex)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label=uptake_l), position = position_dodge(width=0.85),vjust=2) +
  scale_y_continuous(labels=percent) +
  scale_fill_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="")

Combine figure with table for single figure.


uptake_table <- uptake_age_sex %>%
  mutate(resident_population = comma(resident_population),
         examined = comma(examined)) %>%
  rename(Sex = sex,
         Age = age,
         `Resident population` = resident_population,
         Examined = examined) %>%
  mutate(Sex = case_when(Sex=="m" ~ "Male",
                         Sex=="f" ~ "Female"))

uptake_graph / gridExtra::tableGrob(uptake_table, rows = NULL)

ggsave(here("figures/s5.tiff"), height=10)
Saving 7.29 x 10 in image

Uptake by division


uptake_division <- read_xlsx("2024-03-26_mass_xray_uptake.xlsx", sheet = "uptake_division")

division_pops %>%
  filter(year==1957) %>%
  select(division, population_without_inst_ship) %>%
  left_join(uptake_division) %>%
  mutate(pct_pop_examined = examined/population_without_inst_ship)
Joining with `by = join_by(division)`

5 TB case notification rates

5.1 Overall TB case notification rates

Now calculate case notification rates per 100,000 population

Merge the notification and population denominator datasets together.

Here we need to include the whole population (with shipping and institutions) as they are included in the notifications.


overall_inc <- overall_pops %>%
  left_join(cases_by_year)
Joining with `by = join_by(year, year2)`
overall_inc <- overall_inc %>%
  mutate(inc_pulm_100k = pulmonary_notifications/total_population*100000,
         inc_ep_100k = `non-pulmonary_notifications`/total_population*100000,
         inc_100k = total_notifications/total_population*100000)

overall_inc %>%
  select(year, inc_100k, inc_pulm_100k, inc_ep_100k) %>%
  mutate_at(.vars = vars(inc_100k, inc_pulm_100k, inc_ep_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

overall_inc %>%
  select(year2, inc_pulm_100k, inc_ep_100k) %>%
  pivot_longer(cols = c(inc_pulm_100k, `inc_ep_100k`)) %>%
  mutate(name = case_when(name == "inc_pulm_100k" ~ "Pulmonary TB",
                          name == "inc_ep_100k" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

Change in case notification rates pre-intervention

#pre-ACF
overall_inc %>%
  filter(year %in% 1950:1956) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/7)*100)

#post-ACF
overall_inc %>%
  filter(year %in% 1958:1963) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/6)*100)
NA

5.2 TB case notification rates by Division


division_inc <- division_pops %>%
  left_join(cases_by_division)
Joining with `by = join_by(division, year)`
division_inc <- division_inc %>%
  mutate(inc_100k = cases/total_population*100000)

division_inc %>%
  select(year, division, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

division_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

5.2 TB case notification rates by Ward

Here we will filter out the institutions and harbour from the denominators, as we don’t have reliable population denominators for them.


ward_inc <- ward_pops %>%
  left_join(cases_by_ward)
Joining with `by = join_by(ward, year, year2)`
ward_inc <- ward_inc %>%
  mutate(inc_100k = cases/population_without_inst_ship*100000)

ward_inc %>%
  select(year, ward, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
ℹ Please use a list of either functions or lambdas:

# Simple named list: list(mean = mean, median = median)

# Auto named with `tibble::lst()`: tibble::lst(mean, median)

# Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

ward_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Incidence (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA
NA
NA

On a map


st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k)) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_viridis_c(name="Case notification rate (per 100,000)",
                       option = "A") +
  theme_ggdist() +
  theme(legend.position = "top",
        legend.key.width = unit(2, "cm"),
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  guides(fill=guide_colorbar(title.position = "top"))
Joining with `by = join_by(division, ward, ward_number)`

6. TB Mortality

6.1 Overall Mortality

Import the TB mortality data.

First, overall deaths. Note that in the original reports, we have a pulmonary TB death rate per million for all years, and numbers of pulmonary TB deaths for each year apart from 1950.


#get the overall mortality sheets
deaths_sheets <- enframe(all_sheets) %>%
  filter(grepl("deaths", value)) %>%
  pull(value)


overall_deaths <- map_df(deaths_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

overall_deaths %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA
NA

Plot the raw numbers of pulmonary deaths


overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_deaths)) +
  geom_line(colour = "#DE0D92") +
  geom_point(colour = "#DE0D92") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  labs(y="Pulmonary TB deaths per year",
       x = "Year",
       title = "Numbers of pulmonary TB deaths",
       subtitle = "Glasgow, 1950-1963",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: no data for 1950") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_point()`).

Now the incidence of pulmonary TB death

overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_death_rate_per_100k)) +
  geom_line(colour = "#4D6CFA") +
  geom_point(colour = "#4D6CFA") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(y="Annual incidence of death (per 100,000)",
       x = "Year",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

ggsave(here("figures/deaths.tiff"), width=10)
Saving 10 x 4.51 in image

6. Table 1

Make Table 1 here, and save for publication.


overall_pops %>% 
  select(year, total_population) %>%
  left_join(overall_inc %>%
              select(year, 
                     pulmonary_notifications, inc_pulm_100k,
                     `non-pulmonary_notifications`, inc_ep_100k,
                     total_notifications, inc_100k)) %>%
  left_join(overall_deaths %>%
              select(year,
                     pulmonary_deaths, pulmonary_death_rate_per_100k)) %>%
  mutate(percent_pulmonary = percent(pulmonary_notifications/(total_notifications ), accuracy=0.1)) %>%
  mutate(across(where(is.numeric) & !(year),  ~round(., digits=1))) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.)))
Joining with `by = join_by(year)`
Joining with `by = join_by(year)`

Comparison fo age-sex distribution of cases in 1950-1956 vs. 1957


label_abs2 <- function(x) {
  percent(abs(x))
}



cases_by_age_sex %>% 
  ungroup() %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(acf_period, age, sex) %>%
  summarise(cases = sum(cases)) %>%
  ungroup() %>%
  group_by(acf_period) %>%
  mutate(period_total = sum(cases)) %>%
  mutate(pct = cases/period_total) %>%
  mutate(pct2 = case_when(sex=="F" ~ -pct,
                          TRUE ~ pct)) %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")) %>%
  ggplot() +
  geom_vline(aes(xintercept=0), linetype=2) +
  geom_point(aes(x=pct2,y=age, colour=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), stat="identity") +
  scale_x_continuous(labels=label_abs2, limits = c(-0.2, 0.2)) +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA")) +
  theme_grey(base_family = "Aptos") +
  labs(x= "<- Female                Percent of cases              Male ->",
       y="") +
  theme(legend.title = element_blank(),
        legend.position = "bottom")
`summarise()` has grouped output by 'acf_period', 'age'. You can override using the `.groups` argument.
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  Unable to calculate text width/height (using zero)
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  no font could be found for family "Aptos"
ggsave(here("figures/s6.tiff"))
Saving 7.29 x 4.51 in image

Prepare the datasets for modelling


mdata <- ward_inc %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup()


mdata_extrapulmonary <- ward_inc %>%
  filter(tb_type=="Non-Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup() %>% 
  filter(year<=1961) #no data for 1962 and 1963


#scaffold for overall predictions
overall_scaffold <- mdata %>%
    select(year, year2, y_num, acf_period, population_without_inst_ship, ward, cases) %>%
    group_by(year, year2, y_num, acf_period) %>%
    summarise(population_without_inst_ship = sum(population_without_inst_ship),
              cases = sum(cases)) %>%
    ungroup() %>%
    mutate(inc_100k = cases/population_without_inst_ship*100000) %>%
    left_join(mdata_extrapulmonary %>% group_by(year) %>%
                summarise(cases_extrapulmonary = sum(cases))) %>%
    mutate(inc_100k_extrapulmonary = cases_extrapulmonary/population_without_inst_ship*100000)
`summarise()` has grouped output by 'year', 'year2', 'y_num'. You can override using the `.groups` argument.
Joining with `by = join_by(year)`

7. Pulmonary TB model

7.1 Fit the model

Look at the mean and variance of counts (counts of pulmonary notifications are what we are predicting)


#Mean of counts per year
mean(mdata$cases)
[1] 48.32819
#variance of counts per year
var(mdata$cases)
[1] 915.5749

Quite a bit of over-dispersion here, so negative binomial distribution might be a better choice of distributional family than Poisson.

Fit the model with the data


m_pulmonary <- brm(
  cases ~ 0 + Intercept + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata,
                  family = negbinomial(),
                  seed = 1234,
                  threads = threading(2, grainsize = 100, static = TRUE), #for exact reproducibility
                  backend = "cmdstanr",
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1), class=b, coef = "Intercept") +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(4), class=cor),
  control = list(adapt_delta = 0.9))
Model executable is up to date!
Start sampling
Running MCMC with 4 parallel chains, with 2 thread(s) per chain...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 51.5 seconds.
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 finished in 52.7 seconds.
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 finished in 54.7 seconds.
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 66.4 seconds.

All 4 chains finished successfully.
Mean chain execution time: 56.3 seconds.
Total execution time: 66.5 seconds.
  
#check model diagnostics
summary(m_pulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 0 + Intercept + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata (Number of observations: 518) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                             0.25      0.04     0.19     0.33 1.00     1315     1966
sd(y_num)                                                 0.02      0.01     0.01     0.03 1.01      723      477
sd(acf_periodb.acf)                                       0.06      0.04     0.00     0.16 1.00     1722     2034
sd(acf_periodc.postMacf)                                  0.12      0.06     0.01     0.25 1.00      569      912
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.02 1.00     1546     1922
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.02 1.00      539     1484
cor(Intercept,y_num)                                     -0.45      0.19    -0.75     0.00 1.00     2120     2062
cor(Intercept,acf_periodb.acf)                           -0.23      0.29    -0.71     0.37 1.00     3239     3178
cor(y_num,acf_periodb.acf)                               -0.04      0.27    -0.56     0.50 1.00     5062     2991
cor(Intercept,acf_periodc.postMacf)                      -0.14      0.25    -0.58     0.39 1.00     3544     2296
cor(y_num,acf_periodc.postMacf)                           0.10      0.26    -0.42     0.58 1.00     3117     2865
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.06      0.27    -0.48     0.57 1.00     2099     3051
cor(Intercept,y_num:acf_periodb.acf)                     -0.24      0.28    -0.71     0.35 1.00     3201     3417
cor(y_num,y_num:acf_periodb.acf)                         -0.04      0.27    -0.54     0.48 1.00     4993     3153
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.06      0.28    -0.58     0.49 1.00     4171     3668
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.07      0.28    -0.48     0.59 1.00     4087     3237
cor(Intercept,y_num:acf_periodc.postMacf)                -0.02      0.25    -0.50     0.47 1.00     4234     2785
cor(y_num,y_num:acf_periodc.postMacf)                    -0.03      0.27    -0.54     0.48 1.00     2650     2851
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.05      0.27    -0.47     0.55 1.00     2124     2721
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.29    -0.62     0.49 1.00     2255     2817
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.05      0.28    -0.48     0.58 1.00     2090     2548

Regression Coefficients:
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -6.14      0.05    -6.24    -6.04 1.00      813     1321
y_num                         -0.02      0.01    -0.03    -0.01 1.00     2386     2639
acf_periodb.acf                0.02      1.00    -1.98     2.05 1.00     3425     2622
acf_periodc.postMacf           0.04      0.11    -0.17     0.24 1.00     3644     2947
y_num:acf_periodb.acf          0.08      0.13    -0.17     0.33 1.00     3406     2500
y_num:acf_periodc.postMacf    -0.05      0.01    -0.07    -0.03 1.00     3147     2860

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    92.28     20.75    61.22   139.67 1.00     3104     2350

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_pulmonary)


pp_check(m_pulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

prior_summary(m_pulmonary)
                prior class                       coef group resp dpar nlpar lb ub       source
         normal(0, 1)     b                                                                user
         normal(0, 1)     b            acf_periodb.acf                             (vectorized)
         normal(0, 1)     b       acf_periodc.postMacf                             (vectorized)
         normal(0, 1)     b                  Intercept                                     user
         normal(0, 1)     b                      y_num                             (vectorized)
         normal(0, 1)     b      y_num:acf_periodb.acf                             (vectorized)
         normal(0, 1)     b y_num:acf_periodc.postMacf                             (vectorized)
 lkj_corr_cholesky(4)     L                                                                user
 lkj_corr_cholesky(4)     L                             ward                       (vectorized)
       exponential(1)    sd                                                   0            user
       exponential(1)    sd                             ward                  0    (vectorized)
       exponential(1)    sd            acf_periodb.acf  ward                  0    (vectorized)
       exponential(1)    sd       acf_periodc.postMacf  ward                  0    (vectorized)
       exponential(1)    sd                  Intercept  ward                  0    (vectorized)
       exponential(1)    sd                      y_num  ward                  0    (vectorized)
       exponential(1)    sd      y_num:acf_periodb.acf  ward                  0    (vectorized)
       exponential(1)    sd y_num:acf_periodc.postMacf  ward                  0    (vectorized)
    gamma(0.01, 0.01) shape                                                   0            user

Nicer version of trace plots for supplemental material


as_draws_df(m_pulmonary) %>% 
  bayesplot::mcmc_rank_overlay(pars = vars(b_Intercept:shape),
             facet_args = list(ncol = 4)) +
  scale_colour_scico_d(palette = "managua", name = "Chain") +
  theme_ggdist()+
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "top")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

ggsave(here("figures/s8.tiff"), width=16, height=16)

Nicer version of table of parameters for supplement


summarise_draws(m_pulmonary) %>%
  mutate(across(c(mean:ess_tail), comma, accuracy=0.01)) %>%
  write_csv(here("figures/s9_table.csv"))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `across(c(mean:ess_tail), comma, accuracy = 0.01)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

7.2 Summarise change in CNRs

Summarise the posterior in graphical form


f1b <- plot_counterfactual(model_data = overall_scaffold, model = m_pulmonary, 
                           population_denominator = population_without_inst_ship, outcome = inc_100k, grouping_var=NULL,
                           re_formula = NA)
  
f1b
Warning: The S3 guide system was deprecated in ggplot2 3.5.0.
ℹ It has been replaced by a ggproto system that can be extended.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Make this into a figure combined with the map of empirical data


f1a <- st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k), colour="grey98", lwd=0.01) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_scico(name="CNR (per 100,000)",
                       palette = "acton", direction = -1) +
  theme_grey() +
  theme(legend.position = "top",
        #legend.key.width = unit(1, "cm"),
        legend.title.align = 0.5,
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_blank(),
        legend.title = element_text(size=10))
Joining with `by = join_by(division, ward, ward_number)`
Warning: The `legend.title.align` argument of `theme()` is deprecated as of ggplot2 3.5.0.
ℹ Please use theme(legend.title = element_text(hjust)) instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
(f1a / f1b) + plot_annotation(tag_levels = "A")
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
ggsave(here("figures/f1.tiff"), width=7, height=8)
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Summary of change in notifications numerically


overall_change <- summarise_change(model_data=overall_scaffold, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA

7.3 Compared to counterfactual

Numbers of pulmonary TB cases averted compared to counterfactual per year.

Total pulmonary TB cases averted between 1958 and 1963

7.4 Correlation between RR.peak, RR.level, and RR.slope

What are the correlations between peak, level, and slope?


#RR.peak histogram
a <- overall_change$peak_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.peak",
       y="")

#RR. level histogram
b <- overall_change$level_draws  %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.level",
       y="")

#RR.slope histogram
c <- overall_change$slope_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +  
  #scale_x_continuous(limits = c(0, 6)) +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.slope",
       y="")


#Correlation between RR.peak and RR.level
cor_rr_peak_rr_level <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$level_draws$estimate)), digits = 2)

#Correlation between RR.peak and RR.slope
cor_rr_peak_rr_slope <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)

#Correlation between RR.level and RR.slope
cor_rr_level_rr_slope <- round(cor(pluck(overall_change$level_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)


#plot of correlation between RR.peak and RR.level
d <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.level =pluck(overall_change$level_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.level)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.2, x=0.58, label=cor_rr_peak_rr_level), colour="firebrick")  +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.peak and RR.slope
e <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.1, x=0.65, label=cor_rr_peak_rr_slope), colour="firebrick")  +
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.level and RR.slope
f <- bind_cols(RR.level=pluck(overall_change$level_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.level, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=0.75, x=0.65, label=cor_rr_level_rr_slope), colour="firebrick")  +  
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))


(plot_spacer() + plot_spacer() + c) /
  (plot_spacer() + b + f) /
  (a + d + e)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 0.75, x = 0.65, label = cor_rr_level_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 2.2, x = 0.58, label = cor_rr_peak_rr_level),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
Warning in geom_text(aes(y = 2.1, x = 0.65, label = cor_rr_peak_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
ggsave(here("figures/s10.tiff"), width=8, height=8)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 0.75, x = 0.65, label = cor_rr_level_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning in geom_text(aes(y = 2.2, x = 0.58, label = cor_rr_peak_rr_level),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'
Warning in geom_text(aes(y = 2.1, x = 0.65, label = cor_rr_peak_rr_slope),  :
  All aesthetics have length 1, but the data has 4000 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.
`geom_smooth()` using formula = 'y ~ x'

7.5 Ward level pulmonary TB estimates

Plot the counterfactual at ward level


plot_counterfactual(model_data = mdata, model=m_pulmonary, outcome = inc_100k, population_denominator = population_without_inst_ship, 
                    grouping_var = ward, ward, re_formula= ~(1 + y_num*acf_period | ward)) +
  scale_y_continuous(limits = c(0,500))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s7.tiff"), width=16, height=12)
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).

Summary of change in notifications at ward level

Calculate the counterfactual per ward


ward_pulmonary_counterf <- calculate_counterfactual(model_data = mdata, model=m_pulmonary, 
                                                    population_denominator = population_without_inst_ship,
                                                    grouping_var = ward, re_formula=~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
Joining with `by = join_by(year, population_without_inst_ship, .draw, ward)`
Joining with `by = join_by(.draw, ward)`
ward_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Overall counterfactual per ward


ward_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

8. Extra-pulmonary TB notifications

Now we will model the extra-pulmonary TB notification rate. Struggling a bit with negative binomial model, so revert to Poisson.

8.1 Fit the model


m_extrapulmonary <- brm(
  cases ~ 1 + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata_extrapulmonary,
                  family = negbinomial(),
                  seed = 1234,
                  threads = threading(2, grainsize = 100, static = TRUE), #for exact reproducibility
                  backend = "cmdstanr",
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1000), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(2), class=cor))
Compiling Stan program...

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

 
Start sampling
Running MCMC with 4 parallel chains, with 2 thread(s) per chain...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 15.1 seconds.
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 finished in 15.7 seconds.
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 17.2 seconds.
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 finished in 17.4 seconds.

All 4 chains finished successfully.
Mean chain execution time: 16.4 seconds.
Total execution time: 17.7 seconds.
summary(m_extrapulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 1 + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata_extrapulmonary (Number of observations: 444) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                             0.33      0.06     0.22     0.46 1.00     1600     2339
sd(y_num)                                                 0.02      0.01     0.00     0.05 1.01      467     1036
sd(acf_periodb.acf)                                       0.11      0.09     0.00     0.32 1.00     2508     2149
sd(acf_periodc.postMacf)                                  0.12      0.09     0.00     0.33 1.00     1316     1996
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.04 1.00     2043     2059
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.04 1.00      860     1438
cor(Intercept,y_num)                                     -0.11      0.31    -0.65     0.54 1.00     2609     2042
cor(Intercept,acf_periodb.acf)                           -0.01      0.34    -0.63     0.62 1.00     4662     3089
cor(y_num,acf_periodb.acf)                               -0.01      0.33    -0.63     0.63 1.00     3904     3033
cor(Intercept,acf_periodc.postMacf)                      -0.07      0.32    -0.67     0.56 1.00     4270     3035
cor(y_num,acf_periodc.postMacf)                          -0.04      0.33    -0.66     0.60 1.00     3567     3172
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.01      0.33    -0.63     0.63 1.00     3026     2760
cor(Intercept,y_num:acf_periodb.acf)                     -0.02      0.34    -0.64     0.63 1.00     4719     2926
cor(y_num,y_num:acf_periodb.acf)                         -0.01      0.33    -0.62     0.61 1.00     4299     3235
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.07      0.34    -0.69     0.59 1.00     3414     3130
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.01      0.33    -0.61     0.65 1.00     3516     3208
cor(Intercept,y_num:acf_periodc.postMacf)                -0.15      0.31    -0.70     0.49 1.00     3725     3085
cor(y_num,y_num:acf_periodc.postMacf)                    -0.06      0.33    -0.66     0.61 1.00     2789     2903
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.01      0.33    -0.61     0.64 1.00     2638     2658
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.35    -0.70     0.61 1.00     2337     3033
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.01      0.33    -0.62     0.64 1.00     2602     3009

Regression Coefficients:
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -7.92      0.07    -8.07    -7.78 1.00     1581     2598
y_num                         -0.09      0.01    -0.11    -0.07 1.00     4509     3307
acf_periodb.acf               -0.00      0.98    -1.90     1.94 1.00     2635     2429
acf_periodc.postMacf          -0.36      0.39    -1.10     0.38 1.00     2799     2850
y_num:acf_periodb.acf         -0.01      0.12    -0.26     0.23 1.00     2625     2569
y_num:acf_periodc.postMacf     0.02      0.04    -0.05     0.09 1.00     2712     2675

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    93.46     65.58    27.38   269.02 1.00     3751     3259

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_extrapulmonary)

pp_check(m_extrapulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

8.2 Summary of change

Summarise in plot

plot_counterfactual(model_data = overall_scaffold %>% filter(year<=1961), model=m_extrapulmonary, 
                    population_denominator = population_without_inst_ship, outcome=inc_100k_extrapulmonary, re_formula = NA) +
  scale_y_continuous(limits = c(0,50))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s11.tiff"), width=10)
Saving 10 x 4.51 in image
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_line()`).

Summarise numerically.

8.3 Compared to counterfactual

Numbers of extra-pulmonary TB cases averted overall.


overall_ep_counterf <- calculate_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, 
                                               population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`
Joining with `by = join_by(.draw)`
overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

Total extrapulmonary TB cases averted between 1958 and 1963


overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Make into Table 2

bind_rows(
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_ward"),

overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_overall"),

overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB"),

overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB overall")

) %>%
  select(model, year, diff_inc100k, diff_inc100k.lower:rr_inc100k.upper, 
         cases_averted:cases_averted.upper,
         pct_change:pct_change.upper) %>%
  transmute(model=model, year=year,
            diff_cnr = glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr = glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"),
            cases_averted = glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue("{pct_change} ({pct_change.lower} to {pct_change.upper})")) %>%
  write_csv(here("figures/table2.csv"))

8.4 Ward-level extra-pulmonary summaries

Ward-level extra-pulmonary estimates in graphical form.


plot_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, outcome = inc_100k, 
                    population_denominator = population_without_inst_ship, grouping_var = ward,re_formula =~(y_num*acf_period | ward), 
                    ward) + scale_y_continuous(limits= c(0,75))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).
  
ggsave(here("figures/s12.tiff"), width=10, height=12)
Warning: Removed 37 rows containing missing values or values outside the scale range (`geom_line()`).

Numerical summary.


ward_change_extrapulmonary <- summarise_change(model_data = mdata_extrapulmonary, model = m_extrapulmonary, 
                                population_denominator = population_without_inst_ship, grouping_var=ward,
                                re_formula = ~(y_num*acf_period | ward)) 
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw', 'y_num'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change_extrapulmonary  %>%
  keep(names(.) %in% tokeep) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA
NA

9. Age-sex model

9.1 FIt the model

Fit the model

(Not rewritten the functions for this yet)


mdata_age_sex <- cases_by_age_sex %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  mutate(year2 = year+0.5) %>%
  group_by(age, sex) %>%
  mutate(y_num = row_number()) %>%
  ungroup()

m_age_sex <- brm(
  cases ~ y_num + (acf_period)*(age*sex) + (acf_period:y_num)*(age*sex),
                  data = mdata_age_sex,
                  family = negbinomial(),
                  seed = 1234,
                  threads = threading(2, grainsize = 100, static = TRUE), #for exact reproducibility
                  backend = "cmdstanr",
                  chains = 4, cores = 4, 
                  prior = prior(normal(0,1), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b))
Compiling Stan program...

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

 
Start sampling
Running MCMC with 4 parallel chains, with 2 thread(s) per chain...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 20.7 seconds.
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 21.3 seconds.
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 finished in 21.4 seconds.
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 finished in 22.1 seconds.

All 4 chains finished successfully.
Mean chain execution time: 21.4 seconds.
Total execution time: 22.3 seconds.
summary(m_age_sex)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ y_num + (acf_period) * (age * sex) + (acf_period:y_num) * (age * sex) 
   Data: mdata_age_sex (Number of observations: 224) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
                                         Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                                    4.42      0.12     4.18     4.65 1.00     1197     1845
y_num                                       -0.17      0.03    -0.22    -0.11 1.00     1115     1980
acf_periodb.acf                             -0.02      1.00    -1.98     1.90 1.00     7584     3042
acf_periodc.postMacf                        -0.49      0.34    -1.15     0.18 1.00     2231     2993
age06_15                                     0.64      0.15     0.35     0.94 1.00     1626     2728
age16_25                                     1.86      0.14     1.59     2.13 1.00     1519     2215
age26_35                                     1.13      0.14     0.86     1.41 1.00     1542     2736
age36_45                                     0.28      0.15    -0.01     0.59 1.00     1638     2364
age46_55                                    -0.62      0.17    -0.95    -0.29 1.00     1843     2641
age56_65                                    -1.07      0.20    -1.48    -0.68 1.00     2125     2919
age65P                                      -1.63      0.23    -2.08    -1.19 1.00     2909     2586
sexM                                         0.16      0.15    -0.13     0.47 1.00     1157     2173
age06_15:sexM                               -0.43      0.21    -0.84    -0.02 1.00     1725     2787
age16_25:sexM                               -0.58      0.18    -0.94    -0.21 1.00     1469     2310
age26_35:sexM                               -0.34      0.19    -0.71     0.04 1.00     1630     2414
age36_45:sexM                                0.23      0.20    -0.15     0.63 1.00     1672     2734
age46_55:sexM                                1.14      0.22     0.72     1.56 1.00     1805     2634
age56_65:sexM                                1.13      0.23     0.68     1.59 1.00     1797     2570
age65P:sexM                                  0.99      0.27     0.47     1.53 1.00     2395     2384
acf_periodb.acf:age06_15                     0.02      1.00    -1.91     1.95 1.00     7736     2938
acf_periodc.postMacf:age06_15               -0.59      0.51    -1.59     0.40 1.00     3930     3275
acf_periodb.acf:age16_25                     0.02      0.99    -1.91     1.99 1.00     7506     2933
acf_periodc.postMacf:age16_25                0.72      0.44    -0.14     1.58 1.00     3023     3268
acf_periodb.acf:age26_35                     0.03      0.98    -1.84     1.98 1.00     8714     2875
acf_periodc.postMacf:age26_35                0.66      0.44    -0.20     1.50 1.00     3328     2962
acf_periodb.acf:age36_45                     0.04      0.98    -1.88     1.94 1.00     6844     2951
acf_periodc.postMacf:age36_45                0.74      0.46    -0.17     1.65 1.00     3337     3020
acf_periodb.acf:age46_55                     0.06      0.96    -1.81     1.96 1.00     6866     2876
acf_periodc.postMacf:age46_55                0.86      0.49    -0.09     1.82 1.00     3136     2845
acf_periodb.acf:age56_65                     0.03      1.00    -1.91     1.99 1.00     7296     2744
acf_periodc.postMacf:age56_65                0.62      0.53    -0.40     1.64 1.00     3668     2874
acf_periodb.acf:age65P                       0.05      0.98    -1.86     1.96 1.00     8362     3089
acf_periodc.postMacf:age65P                  0.97      0.53    -0.07     2.00 1.00     4386     3439
acf_periodb.acf:sexM                        -0.01      0.98    -1.94     1.91 1.00     7102     3061
acf_periodc.postMacf:sexM                   -0.07      0.37    -0.80     0.66 1.00     2847     2849
y_num:acf_periodb.acf                       -0.10      0.13    -0.35     0.16 1.00     7249     3189
y_num:acf_periodc.postMacf                   0.04      0.04    -0.04     0.12 1.00     1739     2586
acf_periodb.acf:age06_15:sexM               -0.00      0.97    -1.94     1.89 1.00     6763     3129
acf_periodc.postMacf:age06_15:sexM          -0.56      0.63    -1.79     0.70 1.00     4768     3325
acf_periodb.acf:age16_25:sexM                0.02      0.97    -1.90     1.92 1.00     7492     3015
acf_periodc.postMacf:age16_25:sexM           0.66      0.53    -0.38     1.68 1.00     4010     2887
acf_periodb.acf:age26_35:sexM               -0.01      0.98    -1.94     1.90 1.00     7291     3014
acf_periodc.postMacf:age26_35:sexM           0.41      0.51    -0.59     1.42 1.00     3941     3455
acf_periodb.acf:age36_45:sexM                0.00      1.00    -1.98     1.93 1.00     9184     2870
acf_periodc.postMacf:age36_45:sexM           0.11      0.56    -1.00     1.22 1.00     4310     2868
acf_periodb.acf:age46_55:sexM                0.00      1.01    -1.96     2.00 1.00     7928     3071
acf_periodc.postMacf:age46_55:sexM           0.66      0.56    -0.45     1.79 1.00     3987     2962
acf_periodb.acf:age56_65:sexM                0.01      1.00    -1.91     1.96 1.00     8321     3017
acf_periodc.postMacf:age56_65:sexM           0.36      0.57    -0.78     1.48 1.00     4839     3205
acf_periodb.acf:age65P:sexM                 -0.00      1.00    -2.01     1.99 1.00     7452     3023
acf_periodc.postMacf:age65P:sexM             0.26      0.58    -0.85     1.37 1.00     4373     2604
y_num:acf_perioda.preMacf:age06_15           0.02      0.04    -0.06     0.09 1.00     1503     2657
y_num:acf_periodb.acf:age06_15               0.14      0.13    -0.11     0.40 1.00     7445     2650
y_num:acf_periodc.postMacf:age06_15          0.08      0.05    -0.02     0.17 1.00     3532     3361
y_num:acf_perioda.preMacf:age16_25           0.12      0.03     0.06     0.19 1.00     1335     2239
y_num:acf_periodb.acf:age16_25               0.25      0.13    -0.02     0.51 1.00     6976     2891
y_num:acf_periodc.postMacf:age16_25         -0.04      0.04    -0.12     0.04 1.00     2668     3077
y_num:acf_perioda.preMacf:age26_35           0.15      0.03     0.08     0.21 1.00     1292     2718
y_num:acf_periodb.acf:age26_35               0.31      0.13     0.05     0.56 1.00     7542     2977
y_num:acf_periodc.postMacf:age26_35          0.02      0.04    -0.06     0.10 1.00     2784     3047
y_num:acf_perioda.preMacf:age36_45           0.17      0.04     0.09     0.24 1.00     1404     2322
y_num:acf_periodb.acf:age36_45               0.40      0.13     0.14     0.65 1.00     6120     2780
y_num:acf_periodc.postMacf:age36_45          0.06      0.04    -0.03     0.14 1.00     2560     3061
y_num:acf_perioda.preMacf:age46_55           0.19      0.04     0.11     0.27 1.00     1565     2197
y_num:acf_periodb.acf:age46_55               0.44      0.13     0.19     0.69 1.00     6018     2769
y_num:acf_periodc.postMacf:age46_55          0.09      0.04     0.00     0.18 1.00     2778     2862
y_num:acf_perioda.preMacf:age56_65           0.17      0.05     0.08     0.27 1.00     1878     2822
y_num:acf_periodb.acf:age56_65               0.39      0.13     0.13     0.65 1.00     6711     2724
y_num:acf_periodc.postMacf:age56_65          0.11      0.05     0.01     0.20 1.00     3135     2845
y_num:acf_perioda.preMacf:age65P             0.23      0.05     0.13     0.33 1.00     2521     2635
y_num:acf_periodb.acf:age65P                 0.43      0.13     0.17     0.68 1.00     7369     2952
y_num:acf_periodc.postMacf:age65P            0.11      0.05     0.01     0.20 1.00     3127     2939
y_num:acf_perioda.preMacf:sexM               0.01      0.04    -0.06     0.09 1.00     1156     2139
y_num:acf_periodb.acf:sexM                  -0.04      0.14    -0.31     0.23 1.00     6586     3163
y_num:acf_periodc.postMacf:sexM             -0.01      0.04    -0.08     0.06 1.00     2149     2702
y_num:acf_perioda.preMacf:age06_15:sexM      0.00      0.05    -0.10     0.10 1.00     1619     2966
y_num:acf_periodb.acf:age06_15:sexM          0.05      0.14    -0.23     0.32 1.00     6445     3103
y_num:acf_periodc.postMacf:age06_15:sexM     0.10      0.06    -0.02     0.21 1.00     3952     3062
y_num:acf_perioda.preMacf:age16_25:sexM      0.00      0.05    -0.09     0.09 1.00     1354     2117
y_num:acf_periodb.acf:age16_25:sexM          0.06      0.14    -0.21     0.33 1.00     6085     3040
y_num:acf_periodc.postMacf:age16_25:sexM    -0.01      0.05    -0.11     0.09 1.00     3077     3006
y_num:acf_perioda.preMacf:age26_35:sexM     -0.01      0.05    -0.10     0.09 1.00     1497     2292
y_num:acf_periodb.acf:age26_35:sexM          0.06      0.14    -0.22     0.33 1.00     7153     3058
y_num:acf_periodc.postMacf:age26_35:sexM    -0.00      0.05    -0.10     0.09 1.00     3037     3292
y_num:acf_perioda.preMacf:age36_45:sexM     -0.01      0.05    -0.11     0.08 1.00     1569     2256
y_num:acf_periodb.acf:age36_45:sexM          0.00      0.14    -0.27     0.28 1.00     8022     2850
y_num:acf_periodc.postMacf:age36_45:sexM    -0.00      0.05    -0.10     0.10 1.00     3227     3268
y_num:acf_perioda.preMacf:age46_55:sexM     -0.01      0.05    -0.11     0.09 1.00     1615     2599
y_num:acf_periodb.acf:age46_55:sexM          0.00      0.14    -0.27     0.28 1.00     6729     2803
y_num:acf_periodc.postMacf:age46_55:sexM    -0.06      0.05    -0.17     0.04 1.00     3257     3131
y_num:acf_perioda.preMacf:age56_65:sexM      0.05      0.06    -0.06     0.16 1.00     1593     2653
y_num:acf_periodb.acf:age56_65:sexM          0.08      0.14    -0.20     0.35 1.00     7502     3158
y_num:acf_periodc.postMacf:age56_65:sexM     0.02      0.05    -0.09     0.12 1.00     3499     3156
y_num:acf_perioda.preMacf:age65P:sexM        0.00      0.06    -0.12     0.12 1.00     2115     2525
y_num:acf_periodb.acf:age65P:sexM            0.08      0.14    -0.20     0.36 1.00     6531     2903
y_num:acf_periodc.postMacf:age65P:sexM       0.01      0.05    -0.10     0.12 1.00     3303     2920

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape   202.18     67.77   107.63   366.50 1.00     2927     2839

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_age_sex)

pp_check(m_age_sex, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

Summarise posterior


#posterior draws, and summarise
age_sex_summary <- mdata_age_sex %>%
  select(year, year2, y_num, acf_period, age, sex) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))

#create the counterfactual (no intervention), and summarise
age_sex_counterfact <- 
  tibble(year = mdata_age_sex$year,
         year2 = mdata_age_sex$year2,
         y_num = mdata_age_sex$y_num,
         age = mdata_age_sex$age,
         sex = mdata_age_sex$sex,
         acf_period = factor("a. pre-acf")) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention")) %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) 



age_sex_summary %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  ggplot() +
  geom_ribbon(aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
  geom_ribbon(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
  geom_line(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(y=.epred, x=year2, colour="Counterfactual")) +
  geom_line(aes(y=.epred, x=year2, group=acf_period,  colour=acf_period)) +
  geom_point(data = mdata_age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="b. acf" ~ "Counterfactual",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))%>%
                 mutate(acf_period2 = case_when(acf_period=="Before Intervention" ~ "Pre-ACF",
                                               acf_period=="Counterfactual" ~ "ACF",
                                               acf_period=="Post Intervention" ~ "Post-ACF")), 
  aes(y=cases, x=year2, shape=fct_relevel(acf_period2,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  facet_grid(fct_relevel(age, "65 & up y", "56 to 65y", "46 to 55y", "36 to 45y", "26 to 35y", "16 to 25y", "06 to 15y", "0 to 5y")~sex, 
             scales = "free_y") +
  theme_ggdist() +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_shape_discrete(name="Emprical data (period):", na.translate = F) +
  labs(
    x = "Year",
    y = "Case notifications (n)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme(legend.position = "bottom",          
        legend.box="vertical", 
        panel.border = element_rect(colour = "grey78", fill=NA))
  
ggsave(here("figures/s14.tiff"), height=10)
Saving 7.29 x 10 in image

9.2 Summary of impact of intervention

Calculate summary effects


#Peak
out_age_sex_1 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 8),
                      acf_period = c("a. pre-acf", "b. acf"))

peak_draws_age_sex <- add_epred_draws(newdata = out_age_sex_1,
                  object = m_age_sex) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
peak_summary_age_sex <- peak_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")


#Level
 
out_age_sex_2 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 9),
                      acf_period = c("a. pre-acf", "c. post-acf"))
  
level_draws_age_sex <- add_epred_draws(newdata = out_age_sex_2,
                  object = m_age_sex) %>%
    arrange(y_num, .draw) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
level_summary_age_sex <- level_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")

#Slope

out_age_sex_3 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf"))
  
slope_draws_age_sex <- add_epred_draws(newdata = out_age_sex_3,
                  object = m_age_sex) %>%
        arrange(y_num) %>%
        ungroup() %>%
        group_by(.draw, y_num, age, sex) %>%
        summarise(slope = last(.epred)/first(.epred)) %>%
        ungroup() %>%
        group_by(.draw, age, sex) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
`summarise()` has grouped output by '.draw', 'y_num', 'age'. You can override using the `.groups` argument.
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
slope_summary_age_sex <- slope_draws_age_sex %>%
     group_by(age, sex) %>%
      median_qi(estimate) %>%
      mutate(measure = "RR.slope")

Numerical summary of these summary results


bind_rows(
  peak_summary_age_sex, level_summary_age_sex, slope_summary_age_sex
) %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA
NA

As a figure


peak_g_age_sex <- peak_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#level plot
level_g_age_sex <- level_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#slope plot
slope_g_age_sex <- slope_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

9.3 Compared to counterfactual


counterfact_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(year, age, sex, .draw, .epred_counterf = .epred)
Adding missing grouping variables: `year2`, `y_num`, `acf_period`, `.row`
  
#Calcuate predicted number of cases per draw, then summarise.
post_change_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, age, sex, .draw, .epred) 
  
#for the overall period
counterfact_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred)  %>%
      group_by(age, sex, .draw) %>%
      summarise(.epred_counterf = sum(.epred)) %>%
      mutate(year = "Overall (1958-1963)")
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row`
`summarise()` has grouped output by 'age', 'sex'. You can override using the `.groups` argument.
  
#Calcuate incidence per draw, then summarise.
post_change_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred) %>%
      group_by(.draw, age, sex) %>%
      summarise(.epred = sum(.epred)) 
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row`
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
counter_post_overall_age_sex <-
  left_join(counterfact_overall_age_sex, post_change_overall_age_sex) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by(age, sex) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup() %>%
    mutate(year = "Overall (1958-1963)") 
Joining with `by = join_by(age, sex, .draw)`
age_sex_txt <- counter_post_overall_age_sex %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  transmute(year = as.character(year),
            sex = sex,
            age = age,
            cases_averted = glue::glue("{cases_averted}\n({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change}\n({pct_change.lower} to {pct_change.upper})"))


age_sex_txt %>% datatable()
NA
NA

counterfactual_g_age_sex <- counter_post_overall_age_sex %>% 
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_pointrange(aes(x = age, y=cases_averted, ymin=cases_averted.lower, ymax=cases_averted.upper, colour=sex, shape=sex), position=position_dodge(width=0.5)) + 
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  scale_y_continuous(labels = comma) +
  labs(x="",
       y="Number (95% UI)",
       colour="") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "bottom")

counterfactual_g_age_sex

Join together for Figure 3.


(peak_g_age_sex + level_g_age_sex) / (slope_g_age_sex + counterfactual_g_age_sex) + plot_annotation(tag_levels = "A") + plot_layout(guides = "collect") & theme(legend.position = "bottom")

ggsave(here("figures/f3.tiff"), width = 12, height=8)

NA
NA
LS0tCnRpdGxlOiAiR2xhc2dvdyBUQiBBQ0YiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyAxLiBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKIyMjIyAxLjEgTGlicmFyaWVzCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMuCgpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShEVCkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkobWFyZ2luYWxlZmZlY3RzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NpY28pCmxpYnJhcnkoZ2dkZW5zaXR5KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh1bml0cykKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGdnaDR4KQoKYGBgCgojIyMjIDEuMiBIZWxwZXIgZnVuY3Rpb25zCgpGdW5jdGlvbnMgdGhhdCB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoZSBzY3JpcHQKCmBgYHtyfQojbGFiZWxsZXIgZm9yIHllYXJzCnllYXJfbGFiZWxzIDwtIGMoMTk1MDoxOTYzKQoKI1RoZSBHbGFzZ293IG1hc3MgbWludXR1cmUgY2hlc3QgWC1yYXkgY2FtcGFpZ24gaGFwcGVuZWQgYmV0d2VlbiAxMXRoIE1hcmNoIGFuZCAxMnRoIEFwcmlsIDE5NTcKI1NlZ21lbnQgZm9yIGdyYXBocyB0byBtYXRjaCBBQ0YgcGVyaW9kCmFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCmFjZl9lbmQgPC0gZGVjaW1hbF9kYXRlKHltZCgiMTk1Ny0wNC0xMiIpKQoKCmBgYAoKRnVuY3Rpb24gZm9yIGNvdW50ZXJmYWN0dWFsIHBsb3RzCgpgYGB7cn0KCgpwbG90X2NvdW50ZXJmYWN0dWFsIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBvdXRjb21lLCBncm91cGluZ192YXI9TlVMTCwgcmVfZm9ybXVsYSwuLi4pewogIAogICNsYWJlbGxlciBmb3IgeWVhcnMKICB5ZWFyX2xhYmVscyA8LSBjKDE5NTA6MTk2NCkgI2V4dHJhIHllYXIgZm9yIHRoZSBleHRhbnQgb2YgdGhlIHgtYXhpcwoKICAjVGhlIEdsYXNnb3cgbWFzcyBtaW51dHVyZSBjaGVzdCBYLXJheSBjYW1wYWlnbiBoYXBwZW5lZCBiZXR3ZWVuIDExdGggTWFyY2ggYW5kIDEydGggQXByaWwgMTk1NwogICNTZWdtZW50IGZvciBncmFwaHMgdG8gbWF0Y2ggQUNGIHBlcmlvZAogIGFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCiAgYWNmX2VuZCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTA0LTEyIikpCgogIHN1bW1hcnkgPC0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tvdXRjb21lfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgYWRkX2VwcmVkX2RyYXdzKHt7bW9kZWx9fSwgcmVfZm9ybXVsYT17e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBncm91cF9ieSh5ZWFyMiwgYWNmX3BlcmlvZCwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICBtZWFuX3FpKCkgJT4lCiAgICBtdXRhdGUoLmVwcmVkX2luYyA9IC5lcHJlZC97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDAsCiAgICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgIC5lcHJlZF9pbmMudXBwZXIgPSAuZXByZWQudXBwZXIve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAlPiUKICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKCgoKICAjY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbCAobm8gaW50ZXJ2ZW50aW9uKSwgYW5kIHN1bW1hcmlzZQogIAogIGNvdW50ZXJmYWN0IDwtCiAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCB7e291dGNvbWV9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksIHJlX2Zvcm11bGE9e3tyZV9mb3JtdWxhfX0pICU+JQogICAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgbWVhbl9xaSgpICU+JQogICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLAogICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgLmVwcmVkX2luYy51cHBlciA9IC5lcHJlZC51cHBlci97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDApICU+JQogICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKICAKCgogICNwbG90IHRoZSBpbnRlcnZlbnRpb24gZWZmZWN0CnAgPC0gc3VtbWFyeSAlPiUKICAgIGRyb3BsZXZlbHMoKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0LCBsaW5ldHlwZT0iTWFzcyBDWFIgc2NyZWVuaW5nIGludGVydmVudGlvbiIpKSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQsIGxpbmV0eXBlPSJNYXNzIENYUiBzY3JlZW5pbmcgaW50ZXJ2ZW50aW9uIikpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZ3JvdXAgPSBhY2ZfcGVyaW9kLCBmaWxsPWFjZl9wZXJpb2QpLCBhbHBoYT0wLjUpICsKICAgIGdlb21fcmliYm9uKGRhdGEgPSBjb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICAgIGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZmlsbD0iQ291bnRlcmZhY3R1YWwiKSwgYWxwaGE9MC41KSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGNvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgIGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGNvbG91cj0iQ291bnRlcmZhY3R1YWwiKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGdyb3VwPWFjZl9wZXJpb2QsICBjb2xvdXI9YWNmX3BlcmlvZCkpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYi4gYWNmIiB+ICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdC1BQ0YiKSksIAogICAgICAgICAgICAgICBhZXMoeT17e291dGNvbWV9fSwgeD15ZWFyMiwgc2hhcGU9ZmN0X3JlbGV2ZWwoYWNmX3BlcmlvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvc3QtQUNGIikpLCBzaXplPTIpICsKICAgIHRoZW1lX2dyZXkoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hLCBsaW1pdHMgPWMoMCw0MDApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9zaGFwZV9kaXNjcmV0ZShuYW1lPSJFbXBpcmljYWwgZGF0YSAocGVyaW9kKToiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gMiwgbmFtZT0iIikgKwogICAgbGFicygKICAgICAgeCA9ICIiLAogICAgICB5ID0gIkNOUiAocGVyIDEwMCwwMDApIgogICAgKSArCiAgICBndWlkZXMoeCA9ICJheGlzX3RydW5jYXRlZCIsIHkgPSAiYXhpc190cnVuY2F0ZWQiKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgIGxlZ2VuZC5ib3g9InZlcnRpY2FsIiwgCiAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4xLCAnY20nKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkgCgogICAgZmFjZXRfdmFycyA8LSB2YXJzKC4uLikKCiAgaWYgKGxlbmd0aChmYWNldF92YXJzKSAhPSAwKSB7CiAgICBwIDwtIHAgKyBmYWNldF93cmFwKGZhY2V0X3ZhcnMpCiAgfQogIHAKCn0KCmBgYAoKRnVuY3Rpb24gZm9yIGNhbGN1bGF0aW5nICBtZWFzdXJlcyBvZiBjaGFuZ2Ugb3ZlciB0aW1lIChSUi5wZWFrLCBSUi5sZXZlbCwgUlIuc2xvcGUpCgoKYGBge3J9CgpzdW1tYXJpc2VfY2hhbmdlIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBncm91cGluZ192YXIgPSBOVUxMLCByZV9mb3JtdWxhID0gTlVMTCkgewogIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnBlYWsKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTcgdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU3CiAgCiAgZ3JvdXBpbmdfdmFyIDwtIGVucXVvKGdyb3VwaW5nX3ZhcikKICAKICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIAogICAgI21ha2UgdGhlIHByZWRpY3Rpb24gbWF0cml4LCBjb25kaXRpb25hbCBvbiB3aGV0aGVyIHdlIHdhbnQgcmFuZG9tIGVmZmVjdHMgaW5jbHVkZWQgb3Igbm90LgogICAgb3V0IDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0sICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dCA8LSBjcm9zc2luZyh7e21vZGVsX2RhdGF9fSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHlfbnVtKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0KICAKICBwZWFrX2RyYXdzIDwtIGFkZF9lcHJlZF9kcmF3cyhuZXdkYXRhID0gb3V0LAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBtdXRhdGUoZXByZWRfY25yID0gLmVwcmVkL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGdyb3VwX2J5KC5kcmF3LCAhIWdyb3VwaW5nX3ZhcikgJT4lCiAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KGVwcmVkX2NucikvZmlyc3QoZXByZWRfY25yKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5wZWFrIikKICAKICBwZWFrX3N1bW1hcnkgPC0gcGVha19kcmF3cyAlPiUKICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIG1lYW5fcWkoZXN0aW1hdGUpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCiAgCiAgCiAgI2Z1bmN0aW9ucyBmb3IgY2FsY3VsYXRpbmcgUlIubGV2ZWwKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTggdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU4CiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDIgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9IGVsc2UgewogICAgCiAgICBvdXQyIDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0pICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9CiAgCiAgICBsZXZlbF9kcmF3cyA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dDIsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgIGFycmFuZ2UoeV9udW0sIC5kcmF3KSAlPiUKICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKICBsZXZlbF9zdW1tYXJ5IDwtIGxldmVsX2RyYXdzICU+JQogICAgZ3JvdXBfYnkoISFncm91cGluZ192YXIpICU+JQogICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5sZXZlbCIpCiAgICAKICAgIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnNsb3BlCiAgI2kuZS4gcmVsYXRpdmUgY2hhbmdlIGluIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgaW4gMTk1OC0xOTYzIHZzLiBjb3VudGVyZmFjdHVhbCB0cmVuZCBmb3IgMTk1OS0xOTYzCiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gJWluJSBjKDksMTQpKSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJjLiBwb3N0LWFjZiIpCiAgICApCiAgfQogIAogICAgc2xvcGVfZHJhd3MgPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXQzLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgIHN1bW1hcmlzZShzbG9wZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KHNsb3BlKS9maXJzdChzbG9wZSkpICU+JQogICAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAKICBzbG9wZV9zdW1tYXJ5IDwtIHNsb3BlX2RyYXdzICU+JQogICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAgIAogICNnYXRoZXIgYWxsIHRoZSByZXN1bHRzIGludG8gYSBuYW1lZCBsaXN0CiAgICBsc3QocGVha19kcmF3cz1wZWFrX2RyYXdzLCBwZWFrX3N1bW1hcnk9cGVha19zdW1tYXJ5LCAKICAgICAgICBsZXZlbF9kcmF3cz1sZXZlbF9kcmF3cywgbGV2ZWxfc3VtbWFyeT1sZXZlbF9zdW1tYXJ5LCAKICAgICAgICBzbG9wZV9kcmF3cz1zbG9wZV9kcmF3cywgc2xvcGVfc3VtbWFyeT1zbG9wZV9zdW1tYXJ5KQogIAp9CgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgZGlmZmVyZW5jZSBmcm9tIGNvdW50ZXJmYWN0dWFsCgpgYGB7cn0KCmNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbCA8LSBmdW5jdGlvbihtb2RlbF9kYXRhLCBtb2RlbCwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGE9TkEpewogIAogICNlZmZlY3QgdnMuIGNvdW50ZXJmYWN0dWFsCiAgY291bnRlcmZhY3QgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywgeWVhciwge3tncm91cGluZ192YXJ9fSwgYWNmX3BlcmlvZCkgJT4lCiAgICAgIG11dGF0ZSguZXByZWRfaW5jX2NvdW50ZXJmID0gLmVwcmVkL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwgLmVwcmVkX2NvdW50ZXJmPS5lcHJlZCkgICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3QoeWVhciwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIC5kcmF3LCAuZXByZWRfY291bnRlcmYsIC5lcHJlZF9pbmNfY291bnRlcmYsIHt7Z3JvdXBpbmdfdmFyfX0pCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlIDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSwKICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSAlPiUKICAgICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSwgLmRyYXcsIC5lcHJlZCwgLmVwcmVkX2luYywge3tncm91cGluZ192YXJ9fSkgCiAgCiAgI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKICAgIGNvdW50ZXJmYWN0X292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgLmRyYXcsIC5lcHJlZCwge3tncm91cGluZ192YXJ9fSkgICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlX292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIGFjZl9wZXJpb2QpLAogICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgICAgZ3JvdXBfYnkoLmRyYXcsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIC5kcmF3LCAuZXByZWQpICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCiAgCmNvdW50ZXJfcG9zdCA8LQogIGxlZnRfam9pbihjb3VudGVyZmFjdCwgcG9zdF9jaGFuZ2UpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYsCiAgICAgICAgICAgZGlmZl9pbmMxMDBrID0gLmVwcmVkX2luYyAtIC5lcHJlZF9pbmNfY291bnRlcmYsCiAgICAgICAgICAgcnJfaW5jMTAwayA9IC5lcHJlZF9pbmMvLmVwcmVkX2luY19jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSwgZGlmZl9pbmMxMDBrLCBycl9pbmMxMDBrKSAlPiUKICAgIHVuZ3JvdXAoKQoKY291bnRlcl9wb3N0X292ZXJhbGwgPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbCwgcG9zdF9jaGFuZ2Vfb3ZlcmFsbCkgJT4lCiAgICBtdXRhdGUoY2FzZXNfYXZlcnRlZCA9IC5lcHJlZF9jb3VudGVyZi0uZXByZWQsCiAgICAgICAgICAgcGN0X2NoYW5nZSA9ICguZXByZWQgLSAuZXByZWRfY291bnRlcmYpLy5lcHJlZF9jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSkgJT4lCiAgICB1bmdyb3VwKCkKCmxzdChjb3VudGVyX3Bvc3QsIGNvdW50ZXJfcG9zdF9vdmVyYWxsKQoKfQoKCmBgYAoKRnVuY3Rpb24gZm9yIHRpZHlpbmcgdXAgY291bnRlcmZhY3R1YWxzIChtb3N0bHkgZm9yIG1ha2luZyBuaWNlIHRhYmxlcykKCmBgYHtyfQoKdGlkeV9jb3VudGVyZmFjdHVhbHMgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoeWVhciA9IGFzLmNoYXJhY3Rlcih5ZWFyKSwKICAgICAgICAgICAgY2FzZXNfYXZlcnRlZCA9IGdsdWU6OmdsdWUoIntjYXNlc19hdmVydGVkfSAoe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfSAoe3BjdF9jaGFuZ2UubG93ZXJ9IHRvIHtwY3RfY2hhbmdlLnVwcGVyfSkiKSwKICAgICAgICAgICAgZGlmZl9pbmMgPSBnbHVlOjpnbHVlKCJ7ZGlmZl9pbmMxMDBrfSAoe2RpZmZfaW5jMTAway5sb3dlcn0gdG8ge2RpZmZfaW5jMTAway51cHBlcn0pIiksCiAgICAgICAgICAgIHJyX2luYyA9IGdsdWU6OmdsdWUoIntycl9pbmMxMDBrfSAoe3JyX2luYzEwMGsubG93ZXJ9IHRvIHtycl9pbmMxMDBrLnVwcGVyfSkiKSkKfQoKCnRpZHlfY291bnRlcmZhY3R1YWxzX292ZXJhbGwgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZSh5ZWFyID0gYXMuY2hhcmFjdGVyKHllYXIpLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9ICh7Y2FzZXNfYXZlcnRlZC5sb3dlcn0gdG8ge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9KSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZTo6Z2x1ZSgie3BjdF9jaGFuZ2V9ICh7cGN0X2NoYW5nZS5sb3dlcn0gdG8ge3BjdF9jaGFuZ2UudXBwZXJ9KSIpKQp9CgpgYGAKCgoKIyMjIDIuIERhdGEKCkltcG9ydCBkYXRhc2V0cyBmb3IgYW5hbHlzaXMKCiMjIyMgMi4xIFNoYXBlZmlsZXMKCk1ha2UgYSBtYXAgb2YgR2xhc2dvdyB3YXJkcwoKYGBge3J9CgpnbGFzZ293X3dhcmRzXzE5NTEgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL2dsYXNnb3dfd2FyZHNfMTk1MS5nZW9qc29uIikpCgpgYGAKCmBgYHtyfQoKI3JlYWQgaW4gU2NvdGxhbmQgYm91bmRhcnkKc2NvdGxhbmQgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL1Njb3RsYW5kX2JvdW5kYXJ5L1Njb3RsYW5kIGJvdW5kYXJ5LnNocCIpKQoKI21ha2UgYSBib3VuZGluZyBib3ggZm9yIEdsYXNnb3cKYmJveCA8LSBzdF9iYm94KGdsYXNnb3dfd2FyZHNfMTk1MSkgfD4gc3RfYXNfc2ZjKCkKCiNwbG90IHNjb3RsYW5kIHdpdGggYSBib3VuZGluZyBib3ggYXJvdW5kIHRoZSBDaXR5IG9mIEdsYXNnb3cKc2NvdGxhbmRfd2l0aF9iYm94IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBzY290bGFuZCwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIGdlb21fc2YoZGF0YSA9IGJib3gsIGNvbG91ciA9ICIjQzYwQzMwIiwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRUFGN0ZBIiwgc2l6ZSA9IDAuMykpCgojcGxvdCB0aGUgd2FyZHMKI25vdGUgd2UgdGlkeSB1cCBzb21lIG5hbWVzIHRvIGZpdCBvbiBtYXAKZ2xhc2dvd193YXJkX21hcCA8LSBnbGFzZ293X3dhcmRzXzE5NTEgJT4lCiAgbXV0YXRlKHdhcmQgPSBjYXNlX3doZW4od2FyZD09IlNoZXR0bGVzdG9uIGFuZCBUb2xsY3Jvc3MiIH4gIlNoZXR0bGVzdG9uIGFuZFxuVG9sbGNyb3NzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB3YXJkPT0iUGFydGljayAoV2VzdCkiIH4gIlBhcnRpY2tcbihXZXN0KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2FyZD09IlBhcnRpY2sgKEVhc3QpIiB+ICJQYXJ0aWNrXG4oRWFzdCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJOb3J0aCBLZWx2aW4iIH4gIk5vcnRoXG5LZWx2aW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJLaW5uaW5nIFBhcmsiIH4gIktpbm5pbmdcblBhcmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB3YXJkKSkgJT4lCiAgCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9ZGl2aXNpb24pKSArCiAgZ2VvbV9zZl9sYWJlbChhZXMobGFiZWwgPSB3YXJkKSwgc2l6ZT0zLCBmaWxsPU5BLCBsYWJlbC5zaXplID0gTkEsIGNvbG91cj0iYmxhY2siKSArCiAgI3NjYWxlX2NvbG91cl9pZGVudGl0eSgpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJDaXR5IG9mIEdsYXNnb3cgRGl2aXNpb24iKSArCiAgdGhlbWVfZ3JleSgpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSIiLAogICAgICAgZmlsbD0iRGl2aXNpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbnRpcXVld2hpdGUiLCBzaXplID0gMC4zKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXk3OCIpKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41LCB0aXRsZS50aGVtZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpKQoKI2FkZCB0aGUgbWFwIG9mIHNjb3RsYW5kIGFzIGFuIGluc2V0CmdsYXNnb3dfd2FyZF9tYXAgKyBpbnNldF9lbGVtZW50KHNjb3RsYW5kX3dpdGhfYmJveCwgMC43NSwgMCwgMSwgMC40KQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEudGlmZiIpLCBoZWlnaHQ9MTAsIHdpZHRoID0gMTIpCgoKYGBgCgojIyMgMy4gRGVub21pbmF0b3JzCgpMb2FkIGluIHRoZSBkYXRhc2V0cyBmb3IgZGVub25vbWlhdG9ycywgYW5kIGNoZWNrIGZvciBjb25zaXN0ZW5jeS4KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzIDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gIm92ZXJhbGxfcG9wdWxhdGlvbiIpCgpvdmVyYWxsX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKI3NoaWZ0IHllYXIgdG8gbWlkcG9pbnQKb3ZlcmFsbF9wb3BzIDwtIG92ZXJhbGxfcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmBgYAoKTm90ZSwgd2UgaGF2ZSB0aHJlZSBwb3B1bGF0aW9uIGVzdGltYXRlczoKCjEuIFBvcHVsYXRpb24gd2l0aG91dCBpbnN0aXR1dGlvbmFsaXNlZCBwZW9wbGUgb3IgcGVvcGxlIGluIHNoaXBwaW5nCjIuIFBvcHVsYXRpb24gaW4gaW5zdGl0dXRpb25zCjMuIFBvcHVsYXRpb24gaW4gc2hpcHBpbmcKCihQb3B1bGF0aW9uIGluIHNoaXBwaW5nIGlzIGVzdGltYXRlZCBmcm9tIHRoZSAxOTUxIGNlbnN1cywgc28gaXMgdGhlIHNhbWUgZm9yIG1vc3QgeWVhcnMpCgojIyMjIDMuMSBPdmVyYWxsIHBvcHVsYXRpb24KCkZpcnN0LCBwbG90IHRoZSB0b3RhbCBwb3B1bGF0aW9uCgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIpLCBhbHBoYT0wLjUsIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIsIGZpbGw9Im1lZGl1bXNlYWdyZWVuIikgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiksIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogdG90YWwgcG9wdWxhdGlvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkKCgpgYGAKCk5vdyB0aGUgcG9wdWxhdGlvbiBleGNsdWRpbmcgaW5zdGl0dXRpb25hbGlzZWQgYW5kIHNoaXBwaW5nIHBvcHVsYXRpb24KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgeD15ZWFyMiksIGFscGhhPTAuNSwgY29sb3VyID0gInB1cnBsZSIsIGZpbGw9InB1cnBsZSIpICsKICBnZW9tX3BvaW50KGFlcyh5PXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIHg9eWVhcjIpLCBjb2xvdXIgPSAicHVycGxlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBwb3B1bGF0aW9uIGV4Y2x1ZGluZyBpbnN0aXR1dGlvbmFsaXNlZCBhbmQgc2hpcHBpbmciLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpCgoKYGBgCgojIyMjIDMuMiBQb3B1bGF0aW9uIGJ5IFdhcmQKClRoZXJlIGFyZSA1IERpdmlzaW9ucyBjb250YWluaW5nIDM3IFdhcmRzIGluIHRoZSBHbGFzZ293IENvcnBvcmF0aW9uLCB3aXRoIGNvbnNpc3RlbnQgYm91bmRhcmllcyBvdmVyIHRpbWUuCgpgYGB7cn0KI2xvb2stdXAgdGFibGUgZm9yIGRpdmlzaW9ucyBhbmQgd2FyZHMKd2FyZF9sb29rdXAgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAiZGl2aXNpb25zX3dhcmRzIikKCgp3YXJkX3BvcHMgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAid2FyZF9wb3B1bGF0aW9uIikKCndhcmRfcG9wcyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludAp3YXJkX3BvcHMgPC0gd2FyZF9wb3BzICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KQoKI0dldCB0aGUgRGl2aXNpb24gcG9wdWxhdGlvbgpkaXZpc2lvbl9wb3BzIDwtIHdhcmRfcG9wcyAlPiUKICBncm91cF9ieShkaXZpc2lvbiwgeWVhcikgJT4lCiAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgaW5zdGl0dXRpb25zID0gc3VtKGluc3RpdHV0aW9ucywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2hpcHBpbmcgPSBzdW0oc2hpcHBpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHRvdGFsX3BvcHVsYXRpb24gPSBzdW0odG90YWxfcG9wdWxhdGlvbiwgbmEucm0gPSBUUlVFKSkKCmRpdmlzaW9uX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpQbG90IHRoZSBvdmVyYWxsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24gYW5kIFdhcmQKCmBgYHtyfQoKZGl2aXNpb25fcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT10b3RhbF9wb3B1bGF0aW9uLCB4PXllYXIyLCBjb2xvdXI9ZGl2aXNpb24sIGZpbGw9ZGl2aXNpb24pLCBhbHBoYT0wLjgpICsKICBnZW9tX3BvaW50KGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIsIGNvbG91cj1kaXZpc2lvbikpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+LikgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiIikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIiwgbmFtZSA9ICIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IHRvdGFsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24iLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCmBgYHtyfQoKd2FyZF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uLCBmaWxsPWRpdmlzaW9uKSkgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uKSwgY29sb3VyPSJibGFjayIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAod2FyZH4uLCBuY29sPTYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJEaXZpc2lvbiIpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiRGl2aXNpb24iKSArCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczMudGlmZiIpLCBoZWlnaHQ9MTQsIHdpZHRoPTEyKQoKYGBgCgpBcHByb3hpbWF0ZWx5LCBob3cgbWFueSBwZXJzb24teWVhcnMgb2YgZm9sbG93LXVwIGRvIHdlIGhhdmU/CgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyh5ZWFyLCBsZW5ndGgsIC5uYW1lcyA9ICJ5ZWFycyIpLAogICAgICAgICAgICBhY3Jvc3MoYyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB0b3RhbF9wb3B1bGF0aW9uKSwgc3VtKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5kb3VibGUpLCBjb21tYSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpDaGFuZ2UgaW4gcG9wdWxhdGlvbiBieSB3YXJkCgpgYGB7cn0KCndhcmRfcG9wcyAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBzdW1tYXJpc2UocGN0X2NoYW5nZV9wb3AgPSAobGFzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSAtIGZpcnN0KHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApKS9maXJzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSkgJT4lCiAgbXV0YXRlKHBjdF9jaGFuZ2VfcG9wID0gcGVyY2VudChwY3RfY2hhbmdlX3BvcCkpICU+JQogIGFycmFuZ2UocGN0X2NoYW5nZV9wb3ApICU+JQogIGRhdGF0YWJsZSgpCiAgCgoKYGBgCgoKIyMjIyAzLjMgUG9wdWxhdGlvbiBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgphZ2Vfc2V4IDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImFnZV9zZXhfcG9wdWxhdGlvbiIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhtYWxlLCBmZW1hbGUpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXgiKQoKI2NvbGxhcHNlIGRvd24gdG8gc21hbGxlciBhZ2UgZ3JvdXBzIHRvIGJlIG1hbmFnZWFibGUKYWdlX3NleCA8LSBhZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZSA9PSAiMCB0byA0IiB+ICIwMCB0byAwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2UgPT0gIjUgdG8gOSIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxMCB0byAxNCIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxNSB0byAxOSIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyMCB0byAyNCIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyNSB0byAyOSIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzMCB0byAzNCIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzNSB0byAzOSIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0MCB0byA0NCIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0NSB0byA0OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1MCB0byA1NCIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1NSB0byA1OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICI2MCAmIHVwIikpICU+JQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUKICBtdXRhdGUodmFsdWUgPSBzdW0odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCgoKbV9hZ2Vfc2V4IDwtIGxtKHZhbHVlIH4gc3BsaW5lczo6bnMoeWVhciwga25vdHMgPSAzKSphZ2Uqc2V4LCBkYXRhID0gYWdlX3NleCkKCnN1bW1hcnkobV9hZ2Vfc2V4KQoKYWdlX2xldmVscyA8LSBhZ2Vfc2V4ICU+JSBzZWxlY3QoYWdlKSAlPiUgZGlzdGluY3QoKSAlPiUgcHVsbCgpIAoKYWdlX3NleF9uZCA8LSAKICBjcm9zc2luZygKICAgIGFnZT1hZ2VfbGV2ZWxzLAogICAgc2V4PWMoIm1hbGUiLCAiZmVtYWxlIiksCiAgICB5ZWFyID0gMTk1MDoxOTYzCiAgKQoKcHJlZF9wb3BzIDwtIGFnZV9zZXhfbmQgJT4lIG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKG1fYWdlX3NleCkKCnByZWRfcG9wcyAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1wcmVkLCBjb2xvdXI9YWdlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoc2V4fi4pICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIGxpbWl0cyA9IGMoMCwgMTI1MDAwKSkKCiNIb3cgd2VsbCBkbyB0aGV5IG1hdGNoIHVwIHdpdGggb3VyIG92ZXJhbGwgcG9wdWxhdGlvbnM/CnByZWRfcG9wcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2Uoc3VtX3ByZWRfcG9wID0gc3VtKHByZWQpKSAlPiUKICByaWdodF9qb2luKG92ZXJhbGxfcG9wcykgJT4lCiAgc2VsZWN0KHllYXIsIHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikpICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXZhbHVlLCBjb2xvdXI9bmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgbGltaXRzID0gYyg4MDAwMDAsIDEyNTAwMDApKQoKcHJlZF9wb3BzICU+JQogIGdyb3VwX2J5KHllYXIsIHNleCkgJT4lCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShwcmVkKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKHNleF9yYXRpbyA9IGZpcnN0KHN1bSkvbGFzdChzdW0pKQpgYGAKCgoKCgpQb3B1bGF0aW9uIHB5cmFtaWRzCgpgYGB7cn0KCmxhYmVsX2FicyA8LSBmdW5jdGlvbih4KSB7CiAgY29tbWEoYWJzKHgpKQp9CgoKcHJlZF9wb3BzICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUoeWVhcl9wb3AgPSBzdW0ocHJlZCksCiAgICAgICAgIGFnZV9zZXhfcGN0ID0gcGVyY2VudChwcmVkL3llYXJfcG9wLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im1hbGUiIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZmVtYWxlIiB+ICJGZW1hbGUiKSkgJT4lCiAgZ2dwbG90KAogICAgYWVzKHggPSBhZ2UsIGZpbGwgPSBzZXgsIAogICAgICAgIHkgPSBpZmVsc2UodGVzdCA9IHNleCA9PSAiRmVtYWxlIix5ZXMgPSAtcHJlZCwgbm8gPSBwcmVkKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gYWdlX3NleF9wY3QpLAogICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgY29sb3VyPSJibGFjayIsIHNpemU9Mi41KSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2w9NykgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2FicykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikgCgoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczQudGlmZiIpLCB3aWR0aD0xMCkKCgpgYGAKCk5vdCBwZXJmZWN0LCBidXQgcmVzb25hYmx5IGdvb2QuIEJ1dCBhaGhoaGguLi4gdGhlIGFnZSBncm91cHMgZG9uJ3QgYWxpZ24gd2l0aCB0aGUgY2FzZSBub3RpZmljYXRpb24gYWdlIGdyb3VwcyEgQ29tZSBiYWNrIHRvIHRoaW5rIGFib3V0IHRoaXMgbGF0ZXIuCgoKIyMjIDQuIFR1YmVyY3Vsb3NpcyBjYXNlcwoKSW1wb3J0IHRoZSB0dWJlcmN1bG9zaXMgY2FzZXMgZGF0YXNldAoKCiMjIyMgNC4xIE92ZXJhbGwgbm90aWZpY2F0aW9ucwoKT3ZlcmFsbCwgYnkgeWVhci4KCmBgYHtyfQoKY2FzZXNfYnlfeWVhciA8LSByZWFkX3hsc3goIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImJ5X3llYXIiKQoKY2FzZXNfYnlfeWVhciU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV95ZWFyIDwtIGNhc2VzX2J5X3llYXIgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpCgpgYGAKClBsb3QgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIGNhc2Ugbm90aWZpZWQgcGVyIHllYXIsIGJ5IHB1bG1vbmFyeSBhbmQgZXh0cmEgcHVsbW9uYXJ5IGNsYXNzaWZpY2F0aW9uLgoKYGBge3J9CgpjYXNlc19ieV95ZWFyICU+JQogIHNlbGVjdCgtdG90YWxfbm90aWZpY2F0aW9ucywgLXllYXIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2ApKSAlPiUKICBtdXRhdGUobmFtZSA9IGNhc2Vfd2hlbihuYW1lID09ICJwdWxtb25hcnlfbm90aWZpY2F0aW9ucyIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJub24tcHVsbW9uYXJ5X25vdGlmaWNhdGlvbnMiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXZhbHVlLCB4PXllYXIyLCBncm91cCA9IG5hbWUsIGZpbGw9bmFtZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIsIG5hbWU9IiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogVHViZXJjdWxvc2lzIG5vdGlmaWNhdGlvbnMiLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzLCBieSBUQiBkaXNlYXNlIGNsYXNzaWZpY2F0aW9uIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIk51bWJlciBvZiBjYXNlcyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAKCmBgYAoKIyMjIyA0LjIgTm90aWZpY2F0aW9ucyBieSBEaXZpc2lvbgoKUmVhZCBpbiB0aGUgZGF0YXNldHMgYW5kIG1lcmdlIHRvZ2V0aGVyLgoKYGBge3J9CgojbGlzdCBhbGwgdGhlIHNoZWV0cwphbGxfc2hlZXRzIDwtIGV4Y2VsX3NoZWV0cygiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IikKCiNnZXQgdGhlIHdhcmQgc2hlZXRzCndhcmRfc2hlZXRzIDwtIGVuZnJhbWUoYWxsX3NoZWV0cykgJT4lCiAgZmlsdGVyKGdyZXBsKCJieV93YXJkIiwgdmFsdWUpKSAlPiUKICBwdWxsKHZhbHVlKQoKCmNhc2VzX2J5X3dhcmRfc2V4X3llYXIgPC0gbWFwX2RmKHdhcmRfc2hlZXRzLCB+cmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IC4pKQoKY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCkFnZ3JlZ2F0ZSB0b2dldGhlciB0byBnZXQgY2FzZXMgYnkgZGl2aXNpb24KCmBgYHtyfQoKY2FzZXNfYnlfZGl2aXNpb24gPC0gY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBsZWZ0X2pvaW4od2FyZF9sb29rdXApICU+JQogIGdyb3VwX2J5KGRpdmlzaW9uLCB5ZWFyLCB0Yl90eXBlKSAlPiUKICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMsIG5hLnJtID0gVFJVRSkpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV9kaXZpc2lvbiA8LSBjYXNlc19ieV9kaXZpc2lvbiAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV9kaXZpc2lvbiAgJT4lCiAgc2VsZWN0KC15ZWFyMikgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKCmNhc2VzX2J5X2RpdmlzaW9uICU+JQogIG11dGF0ZSh0Yl90eXBlID0gY2FzZV93aGVuKHRiX3R5cGUgPT0gIlB1bG1vbmFyeSIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0Yl90eXBlID09ICJOb24tUHVsbW9uYXJ5IiB+ICJFeHRyYS1wdWxtb25hcnkgVEIiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1jYXNlcywgeD15ZWFyMiwgZ3JvdXAgPSB0Yl90eXBlLCBmaWxsPXRiX3R5cGUpLCBhbHBoYT0wLjUpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+Liwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBub3RpZmljYXRpb25zIGJ5IERpdmlzaW9uIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpgYGAKCiMjIyMgNC4zIE5vdGlmaWNhdGlvbnMgYnkgd2FyZAoKYGBge3J9CgoKY2FzZXNfYnlfd2FyZCA8LSBjYXNlc19ieV93YXJkX3NleF95ZWFyICU+JQogIGdyb3VwX2J5KHdhcmQsIHllYXIsIHRiX3R5cGUpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcywgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV93YXJkICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCiNzaGlmdCB5ZWFyIHRvIG1pZHBvaW50CmNhc2VzX2J5X3dhcmQgPC0gY2FzZXNfYnlfd2FyZCAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmNhc2VzX2J5X3dhcmQgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWNhc2VzLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuOCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4sIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgbm90aWZpY2F0aW9ucyBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCiMjIyMgNC40IE5vdGlmaWNhdGlvbnMgYnkgYWdlIGFuZCBzZXgKCkFzIHdlIGRvbid0IGhhdmUgZGVub21pbmF0b3JzLCB3ZSB3aWxsIGp1c3QgbW9kZWwgdGhlIGNoYW5nZSBpbiBjb3VudHMuCgpgYGB7cn0KCiNsaXN0IGFsbCB0aGUgc2hlZXRzCmFsbF9zaGVldHMgPC0gZXhjZWxfc2hlZXRzKCIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giKQoKI2dldCB0aGUgd2FyZCBzaGVldHMKYWdlX3NleF9zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImJ5X2FnZV9zZXgiLCB2YWx1ZSkpICU+JQogIHB1bGwodmFsdWUpCgoKY2FzZXNfYnlfYWdlX3NleCA8LSBtYXBfZGYoYWdlX3NleF9zaGVldHMsIH5yZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gLikpCgpjYXNlc19ieV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCiMjIyA0LjUgVXB0YWtlIG9mIHNjcmVlbmluZwoKV2hhdCBwZXJjZW50YWdlIG9mIGFkdWx0cyAoMTUrIHBhcnRpY2lwYXRlZCBpbiB0aGUgaW50ZXJ2ZW50aW9uIGluIDE5NTcpPwoKTm90ZSB0aGF0IGluIHRoZSBSZXBvcnQgb2YgU2lyIEtlbm5ldGggQ293YW4sIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBlc3RpbWF0ZXMgb2YgcGFydGljaXBhdGlvbiAod2Ugd2lsbCB1c2UgdGhlc2UgZm9yIHRoZSBtYW51c2NyaXB0LCBhcyB0aGV5IGFyZSBub3QgYmFzZWQgb24gbXkgZXN0aW1hdGVzKQoKYGBge3J9Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbiA8LSAyODE4NzUKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24gPC0gMzQwNDc0Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbiA8LSAzODE3MTMKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24gPC0gNDM3NTg4Cgojb3ZlcmFsbCBwYXJ0aWNpcGF0aW9uCihtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24pLyhtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24pCgojbWFsZSBwYXJ0aWNpcGF0aW9uCm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9tYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24KCiNmZW1hbGUgcGFydGljaXBhdGlvbgpmZW1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9mZW1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbgoKCmBgYAoKCkxvb2sgYXQgdXB0YWtlIG9mIHNjcmVlbmluZyBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgoKdXB0YWtlX2FnZV9zZXggPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9hZ2Vfc2V4IikKCnVwdGFrZV9ncmFwaCA8LSB1cHRha2VfYWdlX3NleCAlPiUKICBtdXRhdGUodXB0YWtlID0gZXhhbWluZWQvcmVzaWRlbnRfcG9wdWxhdGlvbikgJT4lCiAgbXV0YXRlKGV4YW1pbmVkX2wgPSBjb21tYShleGFtaW5lZCksCiAgICAgICAgIHJlc2lkZW50X3BvcHVsYXRpb25fbCA9IGNvbW1hKHJlc2lkZW50X3BvcHVsYXRpb24pLAogICAgICAgICB1cHRha2VfbCA9IHBlcmNlbnQodXB0YWtlLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobGFiZWwgPSBnbHVlKCJ7ZXhhbWluZWRfbH0ve3Jlc2lkZW50X3BvcHVsYXRpb25fbH0gKHt1cHRha2VfbH0pIikpICU+JQogIGZpbHRlcihhZ2UgIT0iMDAtMTQiKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZiIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdChhZXMoeD1hZ2UsIHk9dXB0YWtlLCBncm91cD1zZXgsIGZpbGw9c2V4KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXVwdGFrZV9sKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjg1KSx2anVzdD0yKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1wZXJjZW50KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikKCgoKCmBgYApDb21iaW5lIGZpZ3VyZSB3aXRoIHRhYmxlIGZvciBzaW5nbGUgZmlndXJlLgoKYGBge3J9Cgp1cHRha2VfdGFibGUgPC0gdXB0YWtlX2FnZV9zZXggJT4lCiAgbXV0YXRlKHJlc2lkZW50X3BvcHVsYXRpb24gPSBjb21tYShyZXNpZGVudF9wb3B1bGF0aW9uKSwKICAgICAgICAgZXhhbWluZWQgPSBjb21tYShleGFtaW5lZCkpICU+JQogIHJlbmFtZShTZXggPSBzZXgsCiAgICAgICAgIEFnZSA9IGFnZSwKICAgICAgICAgYFJlc2lkZW50IHBvcHVsYXRpb25gID0gcmVzaWRlbnRfcG9wdWxhdGlvbiwKICAgICAgICAgRXhhbWluZWQgPSBleGFtaW5lZCkgJT4lCiAgbXV0YXRlKFNleCA9IGNhc2Vfd2hlbihTZXg9PSJtIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIFNleD09ImYiIH4gIkZlbWFsZSIpKQoKdXB0YWtlX2dyYXBoIC8gZ3JpZEV4dHJhOjp0YWJsZUdyb2IodXB0YWtlX3RhYmxlLCByb3dzID0gTlVMTCkKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M1LnRpZmYiKSwgaGVpZ2h0PTEwKQoKYGBgCgoKClVwdGFrZSBieSBkaXZpc2lvbgoKYGBge3J9Cgp1cHRha2VfZGl2aXNpb24gPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9kaXZpc2lvbiIpCgpkaXZpc2lvbl9wb3BzICU+JQogIGZpbHRlcih5ZWFyPT0xOTU3KSAlPiUKICBzZWxlY3QoZGl2aXNpb24sIHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApICU+JQogIGxlZnRfam9pbih1cHRha2VfZGl2aXNpb24pICU+JQogIG11dGF0ZShwY3RfcG9wX2V4YW1pbmVkID0gZXhhbWluZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkKCgpgYGAKCgoKCiMjIyA1IFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgojIyMjIDUuMSBPdmVyYWxsIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgpOb3cgY2FsY3VsYXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIHBlciAxMDAsMDAwIHBvcHVsYXRpb24KCk1lcmdlIHRoZSBub3RpZmljYXRpb24gYW5kIHBvcHVsYXRpb24gZGVub21pbmF0b3IgZGF0YXNldHMgdG9nZXRoZXIuCgpIZXJlIHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgd2hvbGUgcG9wdWxhdGlvbiAod2l0aCBzaGlwcGluZyBhbmQgaW5zdGl0dXRpb25zKSBhcyB0aGV5IGFyZSBpbmNsdWRlZCBpbiB0aGUgbm90aWZpY2F0aW9ucy4KCmBgYHtyfQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV95ZWFyKQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9pbmMgJT4lCiAgbXV0YXRlKGluY19wdWxtXzEwMGsgPSBwdWxtb25hcnlfbm90aWZpY2F0aW9ucy90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jX2VwXzEwMGsgPSBgbm9uLXB1bG1vbmFyeV9ub3RpZmljYXRpb25zYC90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jXzEwMGsgPSB0b3RhbF9ub3RpZmljYXRpb25zL3RvdGFsX3BvcHVsYXRpb24qMTAwMDAwKQoKb3ZlcmFsbF9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGluY18xMDBrLCBpbmNfcHVsbV8xMDBrLCBpbmNfZXBfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwaywgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpgYGB7cn0KCm92ZXJhbGxfaW5jICU+JQogIHNlbGVjdCh5ZWFyMiwgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhpbmNfcHVsbV8xMDBrLCBgaW5jX2VwXzEwMGtgKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBjYXNlX3doZW4obmFtZSA9PSAiaW5jX3B1bG1fMTAwayIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJpbmNfZXBfMTAwayIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dmFsdWUsIHg9eWVhcjIsIGdyb3VwID0gbmFtZSwgZmlsbD1uYW1lKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCkNoYW5nZSBpbiBjYXNlIG5vdGlmaWNhdGlvbiByYXRlcyBwcmUtaW50ZXJ2ZW50aW9uCgpgYGB7cn0KI3ByZS1BQ0YKb3ZlcmFsbF9pbmMgJT4lCiAgZmlsdGVyKHllYXIgJWluJSAxOTUwOjE5NTYpICU+JQogIHN1bW1hcmlzZShjaGFuZ2UgPSAoKChsYXN0KGluY19wdWxtXzEwMGspLWZpcnN0KGluY19wdWxtXzEwMGspKS9maXJzdChpbmNfcHVsbV8xMDBrKSkvNykqMTAwKQoKI3Bvc3QtQUNGCm92ZXJhbGxfaW5jICU+JQogIGZpbHRlcih5ZWFyICVpbiUgMTk1ODoxOTYzKSAlPiUKICBzdW1tYXJpc2UoY2hhbmdlID0gKCgobGFzdChpbmNfcHVsbV8xMDBrKS1maXJzdChpbmNfcHVsbV8xMDBrKSkvZmlyc3QoaW5jX3B1bG1fMTAwaykpLzYpKjEwMCkKCmBgYAoKCgoKIyMjIyA1LjIgVEIgY2FzZSBub3RpZmljYXRpb24gcmF0ZXMgYnkgRGl2aXNpb24KCmBgYHtyfQoKZGl2aXNpb25faW5jIDwtIGRpdmlzaW9uX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X2RpdmlzaW9uKQoKCmRpdmlzaW9uX2luYyA8LSBkaXZpc2lvbl9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvdG90YWxfcG9wdWxhdGlvbioxMDAwMDApCgpkaXZpc2lvbl9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGRpdmlzaW9uLCB0Yl90eXBlLCBpbmNfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwayksCiAgICAgICAgICAgIC5mdW5zID0gZnVucyhyb3VuZCkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmRpdmlzaW9uX2luYyAlPiUKICBtdXRhdGUodGJfdHlwZSA9IGNhc2Vfd2hlbih0Yl90eXBlID09ICJQdWxtb25hcnkiIH4gIlB1bG1vbmFyeSBUQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGJfdHlwZSA9PSAiTm9uLVB1bG1vbmFyeSIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9aW5jXzEwMGssIHg9eWVhcjIsIGdyb3VwID0gdGJfdHlwZSwgZmlsbD10Yl90eXBlKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBmYWNldF93cmFwKGRpdmlzaW9ufi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBEaXZpc2lvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1Nylcbk5vdGU6IGV4dHJhLXB1bG1vbmFyeSBUQiBjYXNlcyBieSBEaXZpc2lvbi9XYXJkIG5vdCByZXBvcnRlZCBpbiAxOTYyLTE5NjMiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCiMjIyMgNS4yIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIGJ5IFdhcmQKCkhlcmUgd2Ugd2lsbCBmaWx0ZXIgb3V0IHRoZSBpbnN0aXR1dGlvbnMgYW5kIGhhcmJvdXIgZnJvbSB0aGUgZGVub21pbmF0b3JzLCBhcyB3ZSBkb24ndCBoYXZlIHJlbGlhYmxlIHBvcHVsYXRpb24gZGVub21pbmF0b3JzIGZvciB0aGVtLgoKYGBge3J9Cgp3YXJkX2luYyA8LSB3YXJkX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X3dhcmQpCgoKd2FyZF9pbmMgPC0gd2FyZF9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApCgp3YXJkX2luYyAlPiUKICBzZWxlY3QoeWVhciwgd2FyZCwgdGJfdHlwZSwgaW5jXzEwMGspICU+JQogIG11dGF0ZV9hdCgudmFycyA9IHZhcnMoaW5jXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKCmBgYHtyfQoKd2FyZF9pbmMgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWluY18xMDBrLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJJbmNpZGVuY2UgKHBlciAxMDAsMDAwKSIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBleHRyYS1wdWxtb25hcnkgVEIgY2FzZXMgYnkgRGl2aXNpb24vV2FyZCBub3QgcmVwb3J0ZWQgaW4gMTk2Mi0xOTYzIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgoKYGBgCgpPbiBhIG1hcAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CgpzdF9hc19zZihsZWZ0X2pvaW4od2FyZF9pbmMsIGdsYXNnb3dfd2FyZHNfMTk1MSkpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9aW5jXzEwMGspKSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2wgPSA3KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0iQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAiQSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMiwgImNtIiksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2NvbG9yYmFyKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKQoKCgpgYGAKCgojIyMgNi4gVEIgTW9ydGFsaXR5CgojIyMjIDYuMSBPdmVyYWxsIE1vcnRhbGl0eQoKSW1wb3J0IHRoZSBUQiBtb3J0YWxpdHkgZGF0YS4KCkZpcnN0LCBvdmVyYWxsIGRlYXRocy4gTm90ZSB0aGF0IGluIHRoZSBvcmlnaW5hbCByZXBvcnRzLCB3ZSBoYXZlIGEgcHVsbW9uYXJ5IFRCIGRlYXRoIHJhdGUgcGVyIG1pbGxpb24gZm9yIGFsbCB5ZWFycywgYW5kIG51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGRlYXRocyBmb3IgZWFjaCB5ZWFyIGFwYXJ0IGZyb20gMTk1MC4KCmBgYHtyfQoKI2dldCB0aGUgb3ZlcmFsbCBtb3J0YWxpdHkgc2hlZXRzCmRlYXRoc19zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImRlYXRocyIsIHZhbHVlKSkgJT4lCiAgcHVsbCh2YWx1ZSkKCgpvdmVyYWxsX2RlYXRocyA8LSBtYXBfZGYoZGVhdGhzX3NoZWV0cywgfnJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAuKSkKCm92ZXJhbGxfZGVhdGhzICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgoKYGBgCgpQbG90IHRoZSByYXcgbnVtYmVycyBvZiBwdWxtb25hcnkgZGVhdGhzCgpgYGB7cn0KCm92ZXJhbGxfZGVhdGhzICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXB1bG1vbmFyeV9kZWF0aHMpKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjREUwRDkyIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiNERTBEOTIiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIGxhYnMoeT0iUHVsbW9uYXJ5IFRCIGRlYXRocyBwZXIgeWVhciIsCiAgICAgICB4ID0gIlllYXIiLAogICAgICAgdGl0bGUgPSAiTnVtYmVycyBvZiBwdWxtb25hcnkgVEIgZGVhdGhzIiwKICAgICAgIHN1YnRpdGxlID0gIkdsYXNnb3csIDE5NTAtMTk2MyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBubyBkYXRhIGZvciAxOTUwIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKCmBgYAoKTm93IHRoZSBpbmNpZGVuY2Ugb2YgcHVsbW9uYXJ5IFRCIGRlYXRoCgpgYGB7cn0Kb3ZlcmFsbF9kZWF0aHMgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9cHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjNEQ2Q0ZBIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiM0RDZDRkEiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgbGFicyh5PSJBbm51YWwgaW5jaWRlbmNlIG9mIGRlYXRoIChwZXIgMTAwLDAwMCkiLAogICAgICAgeCA9ICJZZWFyIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9kZWF0aHMudGlmZiIpLCB3aWR0aD0xMCkKCmBgYAoKCiMjIyA2LiBUYWJsZSAxCgpNYWtlIFRhYmxlIDEgaGVyZSwgYW5kIHNhdmUgZm9yIHB1YmxpY2F0aW9uLgoKYGBge3J9CgpvdmVyYWxsX3BvcHMgJT4lIAogIHNlbGVjdCh5ZWFyLCB0b3RhbF9wb3B1bGF0aW9uKSAlPiUKICBsZWZ0X2pvaW4ob3ZlcmFsbF9pbmMgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIAogICAgICAgICAgICAgICAgICAgICBwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgaW5jX3B1bG1fMTAwaywKICAgICAgICAgICAgICAgICAgICAgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2AsIGluY19lcF8xMDBrLAogICAgICAgICAgICAgICAgICAgICB0b3RhbF9ub3RpZmljYXRpb25zLCBpbmNfMTAwaykpICU+JQogIGxlZnRfam9pbihvdmVyYWxsX2RlYXRocyAlPiUKICAgICAgICAgICAgICBzZWxlY3QoeWVhciwKICAgICAgICAgICAgICAgICAgICAgcHVsbW9uYXJ5X2RlYXRocywgcHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSAlPiUKICBtdXRhdGUocGVyY2VudF9wdWxtb25hcnkgPSBwZXJjZW50KHB1bG1vbmFyeV9ub3RpZmljYXRpb25zLyh0b3RhbF9ub3RpZmljYXRpb25zICksIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfnJvdW5kKC4sIGRpZ2l0cz0xKSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkKCmBgYAoKQ29tcGFyaXNvbiBmbyBhZ2Utc2V4IGRpc3RyaWJ1dGlvbiBvZiBjYXNlcyBpbiAxOTUwLTE5NTYgdnMuIDE5NTcKCmBgYHtyfQoKbGFiZWxfYWJzMiA8LSBmdW5jdGlvbih4KSB7CiAgcGVyY2VudChhYnMoeCkpCn0KCgoKY2FzZXNfYnlfYWdlX3NleCAlPiUgCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkoYWNmX3BlcmlvZCwgYWdlLCBzZXgpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShhY2ZfcGVyaW9kKSAlPiUKICBtdXRhdGUocGVyaW9kX3RvdGFsID0gc3VtKGNhc2VzKSkgJT4lCiAgbXV0YXRlKHBjdCA9IGNhc2VzL3BlcmlvZF90b3RhbCkgJT4lCiAgbXV0YXRlKHBjdDIgPSBjYXNlX3doZW4oc2V4PT0iRiIgfiAtcGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBwY3QpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIlByZS1BQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJiLiBhY2YiIH4gIkFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0LUFDRiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD0wKSwgbGluZXR5cGU9MikgKwogIGdlb21fcG9pbnQoYWVzKHg9cGN0Mix5PWFnZSwgY29sb3VyPWZjdF9yZWxldmVsKGFjZl9wZXJpb2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0LUFDRiIpKSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1sYWJlbF9hYnMyLCBsaW1pdHMgPSBjKC0wLjIsIDAuMikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSkgKwogIHRoZW1lX2dyZXkoYmFzZV9mYW1pbHkgPSAiQXB0b3MiKSArCiAgbGFicyh4PSAiPC0gRmVtYWxlICAgICAgICAgICAgICAgIFBlcmNlbnQgb2YgY2FzZXMgICAgICAgICAgICAgIE1hbGUgLT4iLAogICAgICAgeT0iIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M2LnRpZmYiKSkKCmBgYAoKCgpQcmVwYXJlIHRoZSBkYXRhc2V0cyBmb3IgbW9kZWxsaW5nCgpgYGB7cn0KCm1kYXRhIDwtIHdhcmRfaW5jICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkod2FyZCkgJT4lCiAgbXV0YXRlKHlfbnVtID0gcm93X251bWJlcigpKSAlPiUKICB1bmdyb3VwKCkKCgptZGF0YV9leHRyYXB1bG1vbmFyeSA8LSB3YXJkX2luYyAlPiUKICBmaWx0ZXIodGJfdHlwZT09Ik5vbi1QdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBtdXRhdGUoeV9udW0gPSByb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKHllYXI8PTE5NjEpICNubyBkYXRhIGZvciAxOTYyIGFuZCAxOTYzCgoKI3NjYWZmb2xkIGZvciBvdmVyYWxsIHByZWRpY3Rpb25zCm92ZXJhbGxfc2NhZmZvbGQgPC0gbWRhdGEgJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB3YXJkLCBjYXNlcykgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFjZl9wZXJpb2QpICU+JQogICAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCksCiAgICAgICAgICAgICAgY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGxlZnRfam9pbihtZGF0YV9leHRyYXB1bG1vbmFyeSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY2FzZXNfZXh0cmFwdWxtb25hcnkgPSBzdW0oY2FzZXMpKSkgJT4lCiAgICBtdXRhdGUoaW5jXzEwMGtfZXh0cmFwdWxtb25hcnkgPSBjYXNlc19leHRyYXB1bG1vbmFyeS9wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKjEwMDAwMCkKCmBgYAoKCiMjIyA3LiBQdWxtb25hcnkgVEIgbW9kZWwKCiMjIyMgNy4xIEZpdCB0aGUgbW9kZWwKCgpMb29rIGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBvZiBjb3VudHMgKGNvdW50cyBvZiBwdWxtb25hcnkgbm90aWZpY2F0aW9ucyBhcmUgd2hhdCB3ZSBhcmUgcHJlZGljdGluZykKCmBgYHtyfQoKI01lYW4gb2YgY291bnRzIHBlciB5ZWFyCm1lYW4obWRhdGEkY2FzZXMpCiN2YXJpYW5jZSBvZiBjb3VudHMgcGVyIHllYXIKdmFyKG1kYXRhJGNhc2VzKQoKYGBgCgoKUXVpdGUgYSBiaXQgb2Ygb3Zlci1kaXNwZXJzaW9uIGhlcmUsIHNvIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtaWdodCBiZSBhIGJldHRlciBjaG9pY2Ugb2YgZGlzdHJpYnV0aW9uYWwgZmFtaWx5IHRoYW4gUG9pc3Nvbi4KCkZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgZGF0YQoKYGBge3J9CgptX3B1bG1vbmFyeSA8LSBicm0oCiAgY2FzZXMgfiAwICsgSW50ZXJjZXB0ICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIHRocmVhZHMgPSB0aHJlYWRpbmcoMiwgZ3JhaW5zaXplID0gMTAwLCBzdGF0aWMgPSBUUlVFKSwgI2ZvciBleGFjdCByZXByb2R1Y2liaWxpdHkKICAgICAgICAgICAgICAgICAgYmFja2VuZCA9ICJjbWRzdGFuciIsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxKSwgY2xhc3M9YiwgY29lZiA9ICJJbnRlcmNlcHQiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZ2FtbWEoMC4wMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gYikgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGxraig0KSwgY2xhc3M9Y29yKSwKICBjb250cm9sID0gbGlzdChhZGFwdF9kZWx0YSA9IDAuOSkpCiAgCiNjaGVjayBtb2RlbCBkaWFnbm9zdGljcwpzdW1tYXJ5KG1fcHVsbW9uYXJ5KQpwbG90KG1fcHVsbW9uYXJ5KQoKcHBfY2hlY2sobV9wdWxtb25hcnksIHR5cGU9J2VjZGZfb3ZlcmxheScpCnByaW9yX3N1bW1hcnkobV9wdWxtb25hcnkpCgpgYGAKCk5pY2VyIHZlcnNpb24gb2YgdHJhY2UgcGxvdHMgZm9yIHN1cHBsZW1lbnRhbCBtYXRlcmlhbAoKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KCmFzX2RyYXdzX2RmKG1fcHVsbW9uYXJ5KSAlPiUgCiAgYmF5ZXNwbG90OjptY21jX3Jhbmtfb3ZlcmxheShwYXJzID0gdmFycyhiX0ludGVyY2VwdDpzaGFwZSksCiAgICAgICAgICAgICBmYWNldF9hcmdzID0gbGlzdChuY29sID0gNCkpICsKICBzY2FsZV9jb2xvdXJfc2NpY29fZChwYWxldHRlID0gIm1hbmFndWEiLCBuYW1lID0gIkNoYWluIikgKwogIHRoZW1lX2dnZGlzdCgpKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zOC50aWZmIiksIHdpZHRoPTE2LCBoZWlnaHQ9MTYpCmBgYAoKTmljZXIgdmVyc2lvbiBvZiB0YWJsZSBvZiBwYXJhbWV0ZXJzIGZvciBzdXBwbGVtZW50CgpgYGB7cn0KCnN1bW1hcmlzZV9kcmF3cyhtX3B1bG1vbmFyeSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKG1lYW46ZXNzX3RhaWwpLCBjb21tYSwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHdyaXRlX2NzdihoZXJlKCJmaWd1cmVzL3M5X3RhYmxlLmNzdiIpKQoKYGBgCgoKCiMjIyMgNy4yIFN1bW1hcmlzZSBjaGFuZ2UgaW4gQ05ScwoKU3VtbWFyaXNlIHRoZSBwb3N0ZXJpb3IgaW4gZ3JhcGhpY2FsIGZvcm0KCmBgYHtyfQoKZjFiIDwtIHBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsID0gbV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZSA9IGluY18xMDBrLCBncm91cGluZ192YXI9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IE5BKQogIApmMWIKYGBgCgpNYWtlIHRoaXMgaW50byBhIGZpZ3VyZSBjb21iaW5lZCB3aXRoIHRoZSBtYXAgb2YgZW1waXJpY2FsIGRhdGEKCmBgYHtyfQoKZjFhIDwtIHN0X2FzX3NmKGxlZnRfam9pbih3YXJkX2luYywgZ2xhc2dvd193YXJkc18xOTUxKSkgJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1pbmNfMTAwayksIGNvbG91cj0iZ3JleTk4IiwgbHdkPTAuMDEpICsKICBmYWNldF93cmFwKHllYXJ+LiwgbmNvbCA9IDcpICsKICBzY2FsZV9maWxsX3NjaWNvKG5hbWU9IkNOUiAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gImFjdG9uIiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZV9ncmV5KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICNsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxLCAiY20iKSwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgooZjFhIC8gZjFiKSArIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjEudGlmZiIpLCB3aWR0aD03LCBoZWlnaHQ9OCkKCmBgYAoKU3VtbWFyeSBvZiBjaGFuZ2UgaW4gbm90aWZpY2F0aW9ucyBudW1lcmljYWxseQoKYGBge3J9CgpvdmVyYWxsX2NoYW5nZSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGE9b3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3I9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEgPSBOQSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQpvdmVyYWxsX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKICAKYGBgCgoKIyMjIyA3LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCk51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgY29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwgcGVyIHllYXIuCgpgYGB7cn0KCm92ZXJhbGxfcHVsbW9uYXJ5X2NvdW50ZXJmIDwtIGNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpUb3RhbCBwdWxtb25hcnkgVEIgY2FzZXMgYXZlcnRlZCBiZXR3ZWVuIDE5NTggYW5kIDE5NjMKCmBgYHtyfQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKIyMjIyA3LjQgQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrLCBSUi5sZXZlbCwgYW5kIFJSLnNsb3BlCgpXaGF0IGFyZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gcGVhaywgbGV2ZWwsIGFuZCBzbG9wZT8KCmBgYHtyfQoKI1JSLnBlYWsgaGlzdG9ncmFtCmEgPC0gb3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICBsYWJzKHg9IlJSLnBlYWsiLAogICAgICAgeT0iIikKCiNSUi4gbGV2ZWwgaGlzdG9ncmFtCmIgPC0gb3ZlcmFsbF9jaGFuZ2UkbGV2ZWxfZHJhd3MgICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1lc3RpbWF0ZSksIGZpbGw9ImRhcmtibHVlIiwgY29sb3VyPSJkYXJrYmx1ZSIsIGFscGhhPTAuMykrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIubGV2ZWwiLAogICAgICAgeT0iIikKCiNSUi5zbG9wZSBoaXN0b2dyYW0KYyA8LSBvdmVyYWxsX2NoYW5nZSRzbG9wZV9kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIuc2xvcGUiLAogICAgICAgeT0iIikKCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApjb3JfcnJfcGVha19ycl9sZXZlbCA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJGxldmVsX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5zbG9wZQpjb3JfcnJfcGVha19ycl9zbG9wZSA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5sZXZlbCBhbmQgUlIuc2xvcGUKY29yX3JyX2xldmVsX3JyX3Nsb3BlIDwtIHJvdW5kKGNvcihwbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgoKI3Bsb3Qgb2YgY29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApkIDwtIGJpbmRfY29scyhSUi5wZWFrPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHBlYWtfZHJhd3MkZXN0aW1hdGUpLCAKICAgICAgICAgIFJSLmxldmVsID1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSkpICU+JQogIGdncGxvdChhZXMoeT1SUi5wZWFrLCB4ID0gUlIubGV2ZWwpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0yLjIsIHg9MC41OCwgbGFiZWw9Y29yX3JyX3BlYWtfcnJfbGV2ZWwpLCBjb2xvdXI9ImZpcmVicmljayIpICArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNwbG90IG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIuc2xvcGUKZSA8LSBiaW5kX2NvbHMoUlIucGVhaz1wbHVjayhvdmVyYWxsX2NoYW5nZSRwZWFrX2RyYXdzJGVzdGltYXRlKSwgCiAgICAgICAgICBSUi5zbG9wZSA9cGx1Y2sob3ZlcmFsbF9jaGFuZ2Ukc2xvcGVfZHJhd3MkZXN0aW1hdGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHk9UlIucGVhaywgeCA9IFJSLnNsb3BlKSkgKwogIGdlb21faGV4KCkgKwogIGdlb21fc21vb3RoKHNlPUZBTFNFLCBjb2xvdXI9ImZpcmVicmljayIsIG1ldGhvZCA9ICJsbSIpICsKICBnZW9tX3RleHQoYWVzKHk9Mi4xLCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9wZWFrX3JyX3Nsb3BlKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKwogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojcGxvdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLmxldmVsIGFuZCBSUi5zbG9wZQpmIDwtIGJpbmRfY29scyhSUi5sZXZlbD1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIAogICAgICAgICAgUlIuc2xvcGUgPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PVJSLmxldmVsLCB4ID0gUlIuc2xvcGUpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0wLjc1LCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9sZXZlbF9ycl9zbG9wZSksIGNvbG91cj0iZmlyZWJyaWNrIikgICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKKHBsb3Rfc3BhY2VyKCkgKyBwbG90X3NwYWNlcigpICsgYykgLwogIChwbG90X3NwYWNlcigpICsgYiArIGYpIC8KICAoYSArIGQgKyBlKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEwLnRpZmYiKSwgd2lkdGg9OCwgaGVpZ2h0PTgpCgoKCmBgYAoKCiMjIyMgNy41IFdhcmQgbGV2ZWwgcHVsbW9uYXJ5IFRCIGVzdGltYXRlcwoKUGxvdCB0aGUgY291bnRlcmZhY3R1YWwgYXQgd2FyZCBsZXZlbAoKYGBge3J9CgpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wdWxtb25hcnksIG91dGNvbWUgPSBpbmNfMTAwaywgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIAogICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9IHdhcmQsIHdhcmQsIHJlX2Zvcm11bGE9IH4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczcudGlmZiIpLCB3aWR0aD0xNiwgaGVpZ2h0PTEyKQoKYGBgCgpTdW1tYXJ5IG9mIGNoYW5nZSBpbiBub3RpZmljYXRpb25zIGF0IHdhcmQgbGV2ZWwKCmBgYHtyfQoKd2FyZF9jaGFuZ2UgPC0gc3VtbWFyaXNlX2NoYW5nZShtb2RlbF9kYXRhPW1kYXRhLCBtb2RlbD1tX3B1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvcj1wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBncm91cGluZ192YXI9d2FyZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IH4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQp3YXJkX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCiNwbG90IHRoZXNlIGluIGEgZmlndXJlCndhcmRfZWZmZWN0cyA8LSB3YXJkX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRwZWFrX3N1bW1hcnkpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRsZXZlbF9zdW1tYXJ5KSAlPiUKICBiaW5kX3Jvd3Mob3ZlcmFsbF9jaGFuZ2Ukc2xvcGVfc3VtbWFyeSkgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhlc3RpbWF0ZToudXBwZXIpLCAKICAgICAgICAgICAgLmZ1bnMgPSBmdW5zKGFzLm51bWVyaWMpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBtdXRhdGUoZXN0aW1hdGUgPSBhcy5kb3VibGUoZXN0aW1hdGUpKSAlPiUKICBmdWxsX2pvaW4oZ2xhc2dvd193YXJkc18xOTUxKSAlPiUgCiAgbXV0YXRlKHdhcmQyID0gcGFzdGUwKHdhcmRfbnVtYmVyLCAiLiAiLCB3YXJkKSkgJT4lCiAgbXV0YXRlKHdhcmQyID0gY2FzZV93aGVuKGlzLm5hKHdhcmQpIH4gIk92ZXJhbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB3YXJkMikpICU+JQogIHN0X2FzX3NmKCkgCgojZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGNob3JvcGxldGggbWFwcwpwbG90X3dhcmRfZWZmZWN0IDwtIGZ1bmN0aW9uKGRhdGEsIG1lYXN1cmUpewogIHt7ZGF0YX19ICU+JQogIGZpbHRlcihtZWFzdXJlID09IHt7bWVhc3VyZX19KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1lc3RpbWF0ZSkpICsKICBnZW9tX3NmX2xhYmVsKGFlcyhsYWJlbCA9IHdhcmRfbnVtYmVyKSwgc2l6ZT0zLCBmaWxsPU5BLCBsYWJlbC5zaXplID0gTkEsIGNvbG91cj0iYmxhY2siKSArCiAgc2NhbGVfZmlsbF9zY2ljbyh0cmFucz0ibG9nIiwgcGFsZXR0ZSA9ICJyb21hIiwgbWlkcG9pbnQgPSAwLCBsaW1pdHM9YygwLjUsMi4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwgbGFiZWxzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwKICAgICAgICAgICAgICAgICAgIG5hbWU9IlJlbGF0aXZlIHJhdGUiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKwogICAgbGFicyh4PSIiLCB5PSIiKQp9CgojZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGNhdGFwaWxsZXIgcGxvdHMKcGxvdF93YXJkX2NhdCA8LSBmdW5jdGlvbihkYXRhLCBtZWFzdXJlLCBzY2FsZSl7CgogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikgKwogICAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSB7e2RhdGF9fSAlPiUgICAgIAogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKG1lYXN1cmU9PXt7bWVhc3VyZX19KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEod2FyZCkpLAogICAgICAgICAgICAgICAgICAgIGFlcyh5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIAogICAgICAgICAgICAgICAgICAgICAgeD1mY3RfcmVvcmRlcih3YXJkMiwgZXN0aW1hdGUpLCBjb2xvdXI9ZXN0aW1hdGUpKSArCiAgICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IHt7ZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihtZWFzdXJlPT17e21lYXN1cmV9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoaXMubmEod2FyZCkpLAogICAgICAgICAgICAgICAgICAgIGFlcyh5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIAogICAgICAgICAgICAgICAgICAgICAgeD13YXJkMiksIGNvbG91cj0iYmxhY2siKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgc2NhbGVfY29sb3VyX3NjaWNvKHRyYW5zPSJsb2ciLCBwYWxldHRlID0gInJvbWEiLCBtaWRwb2ludCA9IDAsIGxpbWl0cz1jKDAuNSwyLjI1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwgbGFiZWxzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJSZWxhdGl2ZSByYXRlIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKCkgKwogICAgdGhlbWVfZ2dkaXN0KCkgICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICAgIGxhYnMoeCA9ICIiLAogICAgICAgICB5ID0gIlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSsKICAgIGd1aWRlcyh4ID0gImF4aXNfdHJ1bmNhdGVkIiwgeSA9ICJheGlzX3RydW5jYXRlZCIpCn0KCgoKd2FyZF9wZWFrX2kgPC0gcGxvdF93YXJkX2VmZmVjdChkYXRhID0gd2FyZF9lZmZlY3RzLCBtZWFzdXJlID0gIlJSLnBlYWsiKSArIGdndGl0bGUoIlBlYWsgZWZmZWN0IikKd2FyZF9sZXZlbF9pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5sZXZlbCIpICsgZ2d0aXRsZSgiTGV2ZWwgZWZmZWN0IikKd2FyZF9zbG9wZV9pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5zbG9wZSIpICsgZ2d0aXRsZSgiU2xvcGUgZWZmZWN0IikKCndhcmRfcGVha19paSA8LSBwbG90X3dhcmRfY2F0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIucGVhayIpICsgZ2d0aXRsZSgiUGVhayBlZmZlY3QiKQp3YXJkX2xldmVsX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5sZXZlbCIpICsgZ2d0aXRsZSgiTGV2ZWwgZWZmZWN0IikKd2FyZF9zbG9wZV9paSA8LSBwbG90X3dhcmRfY2F0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIuc2xvcGUiKSArIGdndGl0bGUoIlNsb3BlIGVmZmVjdCIpCgpzNCA8LSAod2FyZF9wZWFrX2kgKyB3YXJkX2xldmVsX2kgKyB3YXJkX3Nsb3BlX2kpIC8KICAod2FyZF9wZWFrX2lpICsgd2FyZF9sZXZlbF9paSArIHdhcmRfc2xvcGVfaWkpCgpzNFtbMV1dIDwtIHM0W1sxXV0gKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAnbmV3JykKczRbWzJdXSA8LSBzNFtbMl1dICsgcGxvdF9sYXlvdXQodGFnX2xldmVsID0gJ25ldycpCnM0ICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSBjKCdBJywgJzEnKSkgKyBwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcpICYKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMywgImNtIikpCgoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjIudGlmZiIpLCB3aWR0aCA9IDE2LCBoZWlnaHQ9MTIpCgpgYGAKCkNhbGN1bGF0ZSB0aGUgY291bnRlcmZhY3R1YWwgcGVyIHdhcmQKCmBgYHtyfQoKd2FyZF9wdWxtb25hcnlfY291bnRlcmYgPC0gY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSB3YXJkLCByZV9mb3JtdWxhPX4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkKCndhcmRfcHVsbW9uYXJ5X2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKT3ZlcmFsbCBjb3VudGVyZmFjdHVhbCBwZXIgd2FyZAoKYGBge3J9Cgp3YXJkX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCgoKIyMjIDguIEV4dHJhLXB1bG1vbmFyeSBUQiBub3RpZmljYXRpb25zCgpOb3cgd2Ugd2lsbCBtb2RlbCB0aGUgZXh0cmEtcHVsbW9uYXJ5IFRCIG5vdGlmaWNhdGlvbiByYXRlLiBTdHJ1Z2dsaW5nIGEgYml0IHdpdGggbmVnYXRpdmUgYmlub21pYWwgbW9kZWwsIHNvIHJldmVydCB0byBQb2lzc29uLgoKIyMjIyA4LjEgRml0IHRoZSBtb2RlbAoKYGBge3J9CgptX2V4dHJhcHVsbW9uYXJ5IDwtIGJybSgKICBjYXNlcyB+IDEgKyB5X251bSphY2ZfcGVyaW9kICsgKDEgKyB5X251bSphY2ZfcGVyaW9kIHwgd2FyZCkgKyBvZmZzZXQobG9nKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApKSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IG1kYXRhX2V4dHJhcHVsbW9uYXJ5LAogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBuZWdiaW5vbWlhbCgpLAogICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNCwKICAgICAgICAgICAgICAgICAgdGhyZWFkcyA9IHRocmVhZGluZygyLCBncmFpbnNpemUgPSAxMDAsIHN0YXRpYyA9IFRSVUUpLCAjZm9yIGV4YWN0IHJlcHJvZHVjaWJpbGl0eQogICAgICAgICAgICAgICAgICBiYWNrZW5kID0gImNtZHN0YW5yIiwKICAgICAgICAgICAgICAgICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LAogICAgICAgICAgICAgICAgICBwcmlvciA9IHByaW9yKG5vcm1hbCgwLDEwMDApLCBjbGFzcyA9IEludGVyY2VwdCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGdhbW1hKDAuMDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9IGIpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihleHBvbmVudGlhbCgxKSwgY2xhc3M9c2QpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihsa2ooMiksIGNsYXNzPWNvcikpCgpzdW1tYXJ5KG1fZXh0cmFwdWxtb25hcnkpCnBsb3QobV9leHRyYXB1bG1vbmFyeSkKcHBfY2hlY2sobV9leHRyYXB1bG1vbmFyeSwgdHlwZT0nZWNkZl9vdmVybGF5JykKCmBgYAoKIyMjIyA4LjIgU3VtbWFyeSBvZiBjaGFuZ2UKClN1bW1hcmlzZSBpbiBwbG90CgpgYGB7cn0KcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCAlPiUgZmlsdGVyKHllYXI8PTE5NjEpLCBtb2RlbD1tX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZT1pbmNfMTAwa19leHRyYXB1bG1vbmFyeSwgcmVfZm9ybXVsYSA9IE5BKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MCkpCiAgCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3MxMS50aWZmIiksIHdpZHRoPTEwKQoKYGBgCgpTdW1tYXJpc2UgbnVtZXJpY2FsbHkuCgpgYGB7cn0KCm92ZXJhbGxfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5IDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YT1vdmVyYWxsX3NjYWZmb2xkLCBtb2RlbD1tX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yPXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3Zhcj1OVUxMLCByZV9mb3JtdWxhID0gTkEpCgojd2FudCB0byBrZWVwIHRoZSBzdW1tYXJ5IGVzdGltYXRlcyBoZXJlCnRva2VlcCA8LSBjKCJwZWFrX3N1bW1hcnkiLCAibGV2ZWxfc3VtbWFyeSIsICJzbG9wZV9zdW1tYXJ5IikKCiNzdW1tYXJ5IG1lYXN1cmVzIGluIGEgdGFibGUKb3ZlcmFsbF9jaGFuZ2VfZXh0cmFwdWxtb25hcnkgJT4lCiAga2VlcChuYW1lcyguKSAlaW4lIHRva2VlcCkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCiMjIyMgOC4zIENvbXBhcmVkIHRvIGNvdW50ZXJmYWN0dWFsCgpOdW1iZXJzIG9mIGV4dHJhLXB1bG1vbmFyeSBUQiBjYXNlcyBhdmVydGVkIG92ZXJhbGwuCgpgYGB7cn0KCm92ZXJhbGxfZXBfY291bnRlcmYgPC0gY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwgbW9kZWw9bV9leHRyYXB1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApCgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpUb3RhbCBleHRyYXB1bG1vbmFyeSBUQiBjYXNlcyBhdmVydGVkIGJldHdlZW4gMTk1OCBhbmQgMTk2MwoKYGBge3J9CgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdF9vdmVyYWxsICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCk1ha2UgaW50byBUYWJsZSAyCgoKYGBge3J9CmJpbmRfcm93cygKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShtb2RlbCA9ICJQVEJfd2FyZCIpLAoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobW9kZWwgPSAiUFRCX292ZXJhbGwiKSwKCm92ZXJhbGxfZXBfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShtb2RlbCA9ICJFUFRCIiksCgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdF9vdmVyYWxsICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKG1vZGVsID0gIkVQVEIgb3ZlcmFsbCIpCgopICU+JQogIHNlbGVjdChtb2RlbCwgeWVhciwgZGlmZl9pbmMxMDBrLCBkaWZmX2luYzEwMGsubG93ZXI6cnJfaW5jMTAway51cHBlciwgCiAgICAgICAgIGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwKICAgICAgICAgcGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSAlPiUKICB0cmFuc211dGUobW9kZWw9bW9kZWwsIHllYXI9eWVhciwKICAgICAgICAgICAgZGlmZl9jbnIgPSBnbHVlKCJ7ZGlmZl9pbmMxMDBrfSBbe2RpZmZfaW5jMTAway5sb3dlcn0sIHtkaWZmX2luYzEwMGsudXBwZXJ9XSIpLAogICAgICAgICAgICByciA9IGdsdWUoIntycl9pbmMxMDBrfSBbe3JyX2luYzEwMGsubG93ZXJ9LCB7cnJfaW5jMTAway51cHBlcn1dIiksCiAgICAgICAgICAgIGNhc2VzX2F2ZXJ0ZWQgPSBnbHVlKCJ7Y2FzZXNfYXZlcnRlZH0gW3tjYXNlc19hdmVydGVkLmxvd2VyfSwge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9XSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZSgie3BjdF9jaGFuZ2V9IFt7cGN0X2NoYW5nZS5sb3dlcn0sIHtwY3RfY2hhbmdlLnVwcGVyfV0iKSkgJT4lCiAgd3JpdGVfY3N2KGhlcmUoImZpZ3VyZXMvdGFibGUyLmNzdiIpKQoKCgpgYGAKCgoKCiMjIyMgOC40IFdhcmQtbGV2ZWwgZXh0cmEtcHVsbW9uYXJ5IHN1bW1hcmllcwoKV2FyZC1sZXZlbCBleHRyYS1wdWxtb25hcnkgZXN0aW1hdGVzIGluIGdyYXBoaWNhbCBmb3JtLgoKYGBge3J9CgpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwgbW9kZWw9bV9leHRyYXB1bG1vbmFyeSwgb3V0Y29tZSA9IGluY18xMDBrLCAKICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyID0gd2FyZCxyZV9mb3JtdWxhID1+KHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSwgCiAgICAgICAgICAgICAgICAgICAgd2FyZCkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPSBjKDAsNzUpKQogIApnZ3NhdmUoaGVyZSgiZmlndXJlcy9zMTIudGlmZiIpLCB3aWR0aD0xMCwgaGVpZ2h0PTEyKQoKYGBgCgpOdW1lcmljYWwgc3VtbWFyeS4KCmBgYHtyfQoKd2FyZF9jaGFuZ2VfZXh0cmFwdWxtb25hcnkgPC0gc3VtbWFyaXNlX2NoYW5nZShtb2RlbF9kYXRhID0gbWRhdGFfZXh0cmFwdWxtb25hcnksIG1vZGVsID0gbV9leHRyYXB1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3Zhcj13YXJkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB+KHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkgCgojd2FudCB0byBrZWVwIHRoZSBzdW1tYXJ5IGVzdGltYXRlcyBoZXJlCnRva2VlcCA8LSBjKCJwZWFrX3N1bW1hcnkiLCAibGV2ZWxfc3VtbWFyeSIsICJzbG9wZV9zdW1tYXJ5IikKCiNzdW1tYXJ5IG1lYXN1cmVzIGluIGEgdGFibGUKd2FyZF9jaGFuZ2VfZXh0cmFwdWxtb25hcnkgICU+JQogIGtlZXAobmFtZXMoLikgJWluJSB0b2tlZXApICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCgpgYGAKCgojIyMgOS4gQWdlLXNleCBtb2RlbAoKIyMjIyA5LjEgRkl0IHRoZSBtb2RlbAoKRml0IHRoZSBtb2RlbAoKKE5vdCByZXdyaXR0ZW4gdGhlIGZ1bmN0aW9ucyBmb3IgdGhpcyB5ZXQpCgpgYGB7cn0KCm1kYXRhX2FnZV9zZXggPC0gY2FzZXNfYnlfYWdlX3NleCAlPiUKICBmaWx0ZXIodGJfdHlwZT09IlB1bG1vbmFyeSIpICU+JQogIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKHllYXIgJWluJSBjKDE5NTA6MTk1NikgfiAiYS4gcHJlLWFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1NykgfiAiYi4gYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU4OjE5NjMpIH4gImMuIHBvc3QtYWNmIikpICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KSAlPiUKICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgbXV0YXRlKHlfbnVtID0gcm93X251bWJlcigpKSAlPiUKICB1bmdyb3VwKCkKCm1fYWdlX3NleCA8LSBicm0oCiAgY2FzZXMgfiB5X251bSArIChhY2ZfcGVyaW9kKSooYWdlKnNleCkgKyAoYWNmX3BlcmlvZDp5X251bSkqKGFnZSpzZXgpLAogICAgICAgICAgICAgICAgICBkYXRhID0gbWRhdGFfYWdlX3NleCwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIHRocmVhZHMgPSB0aHJlYWRpbmcoMiwgZ3JhaW5zaXplID0gMTAwLCBzdGF0aWMgPSBUUlVFKSwgI2ZvciBleGFjdCByZXByb2R1Y2liaWxpdHkKICAgICAgICAgICAgICAgICAgYmFja2VuZCA9ICJjbWRzdGFuciIsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwgCiAgICAgICAgICAgICAgICAgIHByaW9yID0gcHJpb3Iobm9ybWFsKDAsMSksIGNsYXNzID0gSW50ZXJjZXB0KSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZ2FtbWEoMC4wMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gYikpCgpzdW1tYXJ5KG1fYWdlX3NleCkKcGxvdChtX2FnZV9zZXgpCnBwX2NoZWNrKG1fYWdlX3NleCwgdHlwZT0nZWNkZl9vdmVybGF5JykKCmBgYAoKU3VtbWFyaXNlIHBvc3RlcmlvcgoKCmBgYHtyfQoKI3Bvc3RlcmlvciBkcmF3cywgYW5kIHN1bW1hcmlzZQphZ2Vfc2V4X3N1bW1hcnkgPC0gbWRhdGFfYWdlX3NleCAlPiUKICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgYWRkX2VwcmVkX2RyYXdzKG1fYWdlX3NleCkgJT4lCiAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIGFnZSwgc2V4KSAlPiUKICBtZWFuX3FpKCkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKCiNjcmVhdGUgdGhlIGNvdW50ZXJmYWN0dWFsIChubyBpbnRlcnZlbnRpb24pLCBhbmQgc3VtbWFyaXNlCmFnZV9zZXhfY291bnRlcmZhY3QgPC0gCiAgdGliYmxlKHllYXIgPSBtZGF0YV9hZ2Vfc2V4JHllYXIsCiAgICAgICAgIHllYXIyID0gbWRhdGFfYWdlX3NleCR5ZWFyMiwKICAgICAgICAgeV9udW0gPSBtZGF0YV9hZ2Vfc2V4JHlfbnVtLAogICAgICAgICBhZ2UgPSBtZGF0YV9hZ2Vfc2V4JGFnZSwKICAgICAgICAgc2V4ID0gbWRhdGFfYWdlX3NleCRzZXgsCiAgICAgICAgIGFjZl9wZXJpb2QgPSBmYWN0b3IoImEuIHByZS1hY2YiKSkgJT4lCiAgYWRkX2VwcmVkX2RyYXdzKG1fYWdlX3NleCkgJT4lCiAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIGFnZSwgc2V4KSAlPiUKICBtZWFuX3FpKCkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSAiTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSAiRiIgfiAiRmVtYWxlIikpIAoKCgphZ2Vfc2V4X3N1bW1hcnkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSAiTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSAiRiIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbj0uZXByZWQubG93ZXIsIHltYXg9LmVwcmVkLnVwcGVyLCB4PXllYXIyLCBncm91cCA9IGFjZl9wZXJpb2QsIGZpbGw9YWNmX3BlcmlvZCksIGFscGhhPTAuNSkgKwogIGdlb21fcmliYm9uKGRhdGEgPSBhZ2Vfc2V4X2NvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgIGFlcyh5bWluPS5lcHJlZC5sb3dlciwgeW1heD0uZXByZWQudXBwZXIsIHg9eWVhcjIsIGZpbGw9IkNvdW50ZXJmYWN0dWFsIiksIGFscGhhPTAuNSkgKwogIGdlb21fbGluZShkYXRhID0gYWdlX3NleF9jb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICBhZXMoeT0uZXByZWQsIHg9eWVhcjIsIGNvbG91cj0iQ291bnRlcmZhY3R1YWwiKSkgKwogIGdlb21fbGluZShhZXMoeT0uZXByZWQsIHg9eWVhcjIsIGdyb3VwPWFjZl9wZXJpb2QsICBjb2xvdXI9YWNmX3BlcmlvZCkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtZGF0YV9hZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbihhY2ZfcGVyaW9kPT0iYS4gcHJlLWFjZiIgfiAiQmVmb3JlIEludGVydmVudGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImIuIGFjZiIgfiAiQ291bnRlcmZhY3R1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0IEludGVydmVudGlvbiIpKSU+JQogICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kMiA9IGNhc2Vfd2hlbihhY2ZfcGVyaW9kPT0iQmVmb3JlIEludGVydmVudGlvbiIgfiAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09IkNvdW50ZXJmYWN0dWFsIiB+ICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJQb3N0IEludGVydmVudGlvbiIgfiAiUG9zdC1BQ0YiKSksIAogIGFlcyh5PWNhc2VzLCB4PXllYXIyLCBzaGFwZT1mY3RfcmVsZXZlbChhY2ZfcGVyaW9kMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvc3QtQUNGIikpLCBzaXplPTIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgZmFjZXRfZ3JpZChmY3RfcmVsZXZlbChhZ2UsICI2NSAmIHVwIHkiLCAiNTYgdG8gNjV5IiwgIjQ2IHRvIDU1eSIsICIzNiB0byA0NXkiLCAiMjYgdG8gMzV5IiwgIjE2IHRvIDI1eSIsICIwNiB0byAxNXkiLCAiMCB0byA1eSIpfnNleCwgCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IikgKwogIHRoZW1lX2dnZGlzdCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0RFMEQ5MiIsICJncmV5NTAiLCAiIzRENkNGQSIpICwgbmFtZT0iTW9kZWwgZXN0aW1hdGVzOiIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiKSArCiAgc2NhbGVfc2hhcGVfZGlzY3JldGUobmFtZT0iRW1wcmljYWwgZGF0YSAocGVyaW9kKToiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgbGFicygKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIkNhc2Ugbm90aWZpY2F0aW9ucyAobikiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KSIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICAgICAgICAgCiAgICAgICAgbGVnZW5kLmJveD0idmVydGljYWwiLCAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQogIApnZ3NhdmUoaGVyZSgiZmlndXJlcy9zMTQudGlmZiIpLCBoZWlnaHQ9MTApCgpgYGAKCiMjIyMgOS4yIFN1bW1hcnkgb2YgaW1wYWN0IG9mIGludGVydmVudGlvbgoKQ2FsY3VsYXRlIHN1bW1hcnkgZWZmZWN0cwoKYGBge3J9CgojUGVhawpvdXRfYWdlX3NleF8xIDwtIGNyb3NzaW5nKG1kYXRhX2FnZV9zZXggJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHlfbnVtLCBhZ2UsIHNleCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gPT0gOCksCiAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKSkKCnBlYWtfZHJhd3NfYWdlX3NleCA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dF9hZ2Vfc2V4XzEsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IG1fYWdlX3NleCkgJT4lCiAgICBncm91cF9ieSguZHJhdywgYWdlLCBzZXgpICU+JQogICAgc3VtbWFyaXNlKGVzdGltYXRlID0gbGFzdCguZXByZWQpL2ZpcnN0KC5lcHJlZCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCiAgCnBlYWtfc3VtbWFyeV9hZ2Vfc2V4IDwtIHBlYWtfZHJhd3NfYWdlX3NleCAlPiUKICAgIGdyb3VwX2J5KGFnZSwgc2V4KSAlPiUKICAgIG1lYW5fcWkoZXN0aW1hdGUpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCgoKI0xldmVsCiAKb3V0X2FnZV9zZXhfMiA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKSkKICAKbGV2ZWxfZHJhd3NfYWdlX3NleCA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dF9hZ2Vfc2V4XzIsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IG1fYWdlX3NleCkgJT4lCiAgICBhcnJhbmdlKHlfbnVtLCAuZHJhdykgJT4lCiAgICBncm91cF9ieSguZHJhdywgYWdlLCBzZXgpICU+JQogICAgc3VtbWFyaXNlKGVzdGltYXRlID0gbGFzdCguZXByZWQpL2ZpcnN0KC5lcHJlZCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIubGV2ZWwiKQogIApsZXZlbF9zdW1tYXJ5X2FnZV9zZXggPC0gbGV2ZWxfZHJhd3NfYWdlX3NleCAlPiUKICAgIGdyb3VwX2J5KGFnZSwgc2V4KSAlPiUKICAgIG1lYW5fcWkoZXN0aW1hdGUpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIubGV2ZWwiKQoKI1Nsb3BlCgpvdXRfYWdlX3NleF8zIDwtIGNyb3NzaW5nKG1kYXRhX2FnZV9zZXggJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHlfbnVtLCBhZ2UsIHNleCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gJWluJSBjKDksMTQpKSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJjLiBwb3N0LWFjZiIpKQogIApzbG9wZV9kcmF3c19hZ2Vfc2V4IDwtIGFkZF9lcHJlZF9kcmF3cyhuZXdkYXRhID0gb3V0X2FnZV9zZXhfMywKICAgICAgICAgICAgICAgICAgb2JqZWN0ID0gbV9hZ2Vfc2V4KSAlPiUKICAgICAgICBhcnJhbmdlKHlfbnVtKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsIHlfbnVtLCBhZ2UsIHNleCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKHNsb3BlID0gbGFzdCguZXByZWQpL2ZpcnN0KC5lcHJlZCkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBncm91cF9ieSguZHJhdywgYWdlLCBzZXgpICU+JQogICAgICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3Qoc2xvcGUpL2ZpcnN0KHNsb3BlKSkgJT4lCiAgICAgICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIuc2xvcGUiKQogIApzbG9wZV9zdW1tYXJ5X2FnZV9zZXggPC0gc2xvcGVfZHJhd3NfYWdlX3NleCAlPiUKICAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICAgIG1lZGlhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKCmBgYAoKCk51bWVyaWNhbCBzdW1tYXJ5IG9mIHRoZXNlIHN1bW1hcnkgcmVzdWx0cwoKYGBge3J9CgpiaW5kX3Jvd3MoCiAgcGVha19zdW1tYXJ5X2FnZV9zZXgsIGxldmVsX3N1bW1hcnlfYWdlX3NleCwgc2xvcGVfc3VtbWFyeV9hZ2Vfc2V4CikgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgoKCmBgYAoKQXMgYSBmaWd1cmUKCmBgYHtyfQoKcGVha19nX2FnZV9zZXggPC0gcGVha19zdW1tYXJ5X2FnZV9zZXggJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09IkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTEpLCBsaW5ldHlwZT0yKSsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHg9YWdlLCB5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIGdyb3VwPXNleCwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwKICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9IlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojbGV2ZWwgcGxvdApsZXZlbF9nX2FnZV9zZXggPC0gbGV2ZWxfc3VtbWFyeV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0iTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikrCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4PWFnZSwgeT1lc3RpbWF0ZSwgeW1pbj0ubG93ZXIsIHltYXg9LnVwcGVyLCBncm91cD1zZXgsIGNvbG91cj1zZXgsIHNoYXBlPXNleCksCiAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjUpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjQ0Q3QUM1IiwgImNhZGV0Ymx1ZTMiKSwgbmFtZT0iIikgKwogIHNjYWxlX3NoYXBlKG5hbWU9IiIpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSJSZWxhdGl2ZSByYXRlICg5NSUgVUkpIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKI3Nsb3BlIHBsb3QKc2xvcGVfZ19hZ2Vfc2V4IDwtIHNsb3BlX3N1bW1hcnlfYWdlX3NleCAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9MSksIGxpbmV0eXBlPTIpKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeD1hZ2UsIHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgZ3JvdXA9c2V4LCBjb2xvdXI9c2V4LCBzaGFwZT1zZXgpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICBzY2FsZV9zaGFwZShuYW1lPSIiKSArCiAgbGFicyh4PSIiLAogICAgICAgeT0iUmVsYXRpdmUgcmF0ZSAoOTUlIFVJKSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCgpgYGAKCgojIyMjIDkuMyBDb21wYXJlZCB0byBjb3VudGVyZmFjdHVhbAoKYGBge3J9Cgpjb3VudGVyZmFjdF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIikpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgc2VsZWN0KHllYXIsIGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkX2NvdW50ZXJmID0gLmVwcmVkKQogIAojQ2FsY3VhdGUgcHJlZGljdGVkIG51bWJlciBvZiBjYXNlcyBwZXIgZHJhdywgdGhlbiBzdW1tYXJpc2UuCnBvc3RfY2hhbmdlX2FnZV9zZXggPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IG1fYWdlX3NleCwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBtZGF0YV9hZ2Vfc2V4ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhZ2UsIHNleCwgYWNmX3BlcmlvZCkpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3QoeWVhciwgYWdlLCBzZXgsIC5kcmF3LCAuZXByZWQpIAogIAojZm9yIHRoZSBvdmVyYWxsIHBlcmlvZApjb3VudGVyZmFjdF9vdmVyYWxsX2FnZV9zZXggPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IG1fYWdlX3NleCwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBtZGF0YV9hZ2Vfc2V4ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhZ2UsIHNleCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gImEuIHByZS1hY2YiKSkgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICBzZWxlY3QoYWdlLCBzZXgsIC5kcmF3LCAuZXByZWQpICAlPiUKICAgICAgZ3JvdXBfYnkoYWdlLCBzZXgsIC5kcmF3KSAlPiUKICAgICAgc3VtbWFyaXNlKC5lcHJlZF9jb3VudGVyZiA9IHN1bSguZXByZWQpKSAlPiUKICAgICAgbXV0YXRlKHllYXIgPSAiT3ZlcmFsbCAoMTk1OC0xOTYzKSIpCiAgCiNDYWxjdWF0ZSBpbmNpZGVuY2UgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgpwb3N0X2NoYW5nZV9vdmVyYWxsX2FnZV9zZXggPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IG1fYWdlX3NleCwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBtZGF0YV9hZ2Vfc2V4ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhZ2UsIHNleCwgYWNmX3BlcmlvZCkpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgc2VsZWN0KGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAlPiUKICAgICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgICAgc3VtbWFyaXNlKC5lcHJlZCA9IHN1bSguZXByZWQpKSAKICAKY291bnRlcl9wb3N0X292ZXJhbGxfYWdlX3NleCA8LQogIGxlZnRfam9pbihjb3VudGVyZmFjdF9vdmVyYWxsX2FnZV9zZXgsIHBvc3RfY2hhbmdlX292ZXJhbGxfYWdlX3NleCkgJT4lCiAgICBtdXRhdGUoY2FzZXNfYXZlcnRlZCA9IC5lcHJlZF9jb3VudGVyZi0uZXByZWQsCiAgICAgICAgICAgcGN0X2NoYW5nZSA9ICguZXByZWQgLSAuZXByZWRfY291bnRlcmYpLy5lcHJlZF9jb3VudGVyZikgJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGNhc2VzX2F2ZXJ0ZWQsIHBjdF9jaGFuZ2UpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgbXV0YXRlKHllYXIgPSAiT3ZlcmFsbCAoMTk1OC0xOTYzKSIpIAoKCmFnZV9zZXhfdHh0IDwtIGNvdW50ZXJfcG9zdF9vdmVyYWxsX2FnZV9zZXggJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICB0cmFuc211dGUoeWVhciA9IGFzLmNoYXJhY3Rlcih5ZWFyKSwKICAgICAgICAgICAgc2V4ID0gc2V4LAogICAgICAgICAgICBhZ2UgPSBhZ2UsCiAgICAgICAgICAgIGNhc2VzX2F2ZXJ0ZWQgPSBnbHVlOjpnbHVlKCJ7Y2FzZXNfYXZlcnRlZH1cbih7Y2FzZXNfYXZlcnRlZC5sb3dlcn0gdG8ge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9KSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZTo6Z2x1ZSgie3BjdF9jaGFuZ2V9XG4oe3BjdF9jaGFuZ2UubG93ZXJ9IHRvIHtwY3RfY2hhbmdlLnVwcGVyfSkiKSkKCgphZ2Vfc2V4X3R4dCAlPiUgZGF0YXRhYmxlKCkKCgpgYGAKCmBgYHtyfQoKY291bnRlcmZhY3R1YWxfZ19hZ2Vfc2V4IDwtIGNvdW50ZXJfcG9zdF9vdmVyYWxsX2FnZV9zZXggJT4lIAogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0iTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4ID0gYWdlLCB5PWNhc2VzX2F2ZXJ0ZWQsIHltaW49Y2FzZXNfYXZlcnRlZC5sb3dlciwgeW1heD1jYXNlc19hdmVydGVkLnVwcGVyLCBjb2xvdXI9c2V4LCBzaGFwZT1zZXgpLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjUpKSArIAogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICBzY2FsZV9zaGFwZShuYW1lPSIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh4PSIiLAogICAgICAgeT0iTnVtYmVyICg5NSUgVUkpIiwKICAgICAgIGNvbG91cj0iIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKY291bnRlcmZhY3R1YWxfZ19hZ2Vfc2V4CmBgYAoKSm9pbiB0b2dldGhlciBmb3IgRmlndXJlIDMuCgoKYGBge3J9CgoocGVha19nX2FnZV9zZXggKyBsZXZlbF9nX2FnZV9zZXgpIC8gKHNsb3BlX2dfYWdlX3NleCArIGNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleCkgKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICJBIikgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpICYgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9mMy50aWZmIiksIHdpZHRoID0gMTIsIGhlaWdodD04KQoKCmBgYAoKCg==